<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://sukksa.github.io/</id>
  <title type="text">Sukksa&apos;s Blog</title>
  <subtitle type="text">I Love the World and Everything In It</subtitle>
  <updated>2026-05-31T22:49:21.000Z</updated>
  <author><name>Sukksa</name></author>
  <link rel="alternate" href="https://sukksa.github.io/"/>
  <link rel="self" href="https://sukksa.github.io/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://sukksa.github.io/posts/react-hooks/</id>
      <title type="text">react-hooks</title>
      <published>2026-05-31T22:49:21.000Z</published>
      <updated>2026-05-31T22:49:21.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/react-hooks/"/>
      <summary type="text">React 的核心是组件。v16.8 版本之前，组件的标准写法是类（class）。 任何一个组件，可以用类来写，也可以用钩子来写。下面是类的写法。 再来看钩子的写法，也就是函数。 官方推荐使用钩子（函数），而不是类。因为钩子更简洁，代码量少，用起来比较&quot;轻&quot;，而类比较&quot;重&quot;。而且，钩子是函数，更符合…</summary>
      <content type="html"><![CDATA[<section><h1>React Hooks<a href="#react-hooks"><span>#</span></a></h1><p>React 的核心是组件。v16.8 版本之前，组件的标准写法是类（class）。</p><p>任何一个组件，可以用类来写，也可以用钩子来写。下面是类的写法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Welcome</span><span> </span><span>extends</span><span> </span><span>React</span><span>.</span><span>Component</span><span> {</span></div></div><div><div><div>2</div></div><div><span> </span><span>render</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>   </span><span>return</span><span> </span><span>&lt;</span><span><span>h1Hello</span><span>, {this.props.</span><span>name</span><span>}</span></span><span>&lt;/</span><span><span>h1</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>再来看钩子的写法，也就是函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Welcome</span><span><span>(</span><span>props</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span> </span><span>return</span><span> </span><span>&lt;</span><span><span>h1Hello</span><span>, {props.</span><span>name</span><span>}</span></span><span>&lt;/</span><span><span>h1</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>官方<a href="https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both" target="_blank">推荐</a>使用钩子（函数），而不是类。因为钩子更简洁，代码量少，用起来比较”轻”，而类比较”重”。而且，钩子是函数，更符合 React 函数式的本质。</p><section><h2>类和函数的差异<a href="#类和函数的差异"><span>#</span></a></h2><p>类组件和函数组件是有差异的。不同的写法，代表了不同的编程方法论。</p><p><strong>类（class）是数据和逻辑的封装。</strong> 也就是说，组件的状态和操作方法是封装在一起的。如果选择了类的写法，就应该把相关的数据和操作，都写在同一个 class 里面。</p><p><strong>函数一般来说，只应该做一件事，就是返回一个值。</strong> 如果你有多个操作，每个操作应该写成一个单独的函数。而且，数据的状态应该与操作方法分离。根据这种理念，React 的函数组件只应该做一件事情：返回组件的 HTML 代码，而没有其他的功能。这种只进行单纯的数据计算（换算）的函数，在函数式编程里面称为 <strong>“纯函数”</strong>（pure function）。</p><p>但是，这种写法有重大限制，必须是纯函数，不能包含状态，也不支持生命周期方法，因此无法取代类。</p><p><strong>React Hooks 的设计目的，就是加强版函数组件，完全不使用”类”，就能写出一个全功能的组件。</strong></p></section><section><h2>Hook 的含义<a href="#hook-的含义"><span>#</span></a></h2><p>Hook 这个单词的意思是”钩子”。</p><p><strong>React Hooks 的意思是，组件尽量写成纯函数，如果需要外部功能和副作用，就用钩子把外部代码”钩”进来。</strong> React Hooks 就是那些钩子。</p><p>你需要什么功能，就使用什么钩子。React 默认提供了一些常用钩子，你也可以封装自己的钩子。</p><p>所有的钩子都是为函数引入外部功能，所以 React 约定，钩子一律使用 <code>use</code> 前缀命名，便于识别。你要使用 xxx 功能，钩子就命名为 useXxx。</p>

<table><thead><tr><th>Hook</th><th>用途</th></tr></thead><tbody><tr><td><code>useState</code></td><td>在函数组件中添加局部状态</td></tr><tr><td><code>useEffect</code></td><td>执行副作用（数据获取、订阅、DOM 操作等），可替代生命周期方法</td></tr><tr><td><code>useContext</code></td><td>订阅 React Context，方便跨组件共享数据</td></tr><tr><td><code>useReducer</code></td><td>对复杂状态逻辑使用 reducer 模式，类似 Redux</td></tr><tr><td><code>useCallback</code></td><td>记忆化回调函数，避免不必要的重新创建与渲染</td></tr><tr><td><code>useMemo</code></td><td>记忆化计算结果，性能优化</td></tr><tr><td><code>useRef</code></td><td>持有可变引用（DOM 节点、保存任意可变值）</td></tr><tr><td><code>useImperativeHandle</code></td><td>自定义暴露给父组件的实例值（配合 <code>forwardRef</code>）</td></tr><tr><td><code>useLayoutEffect</code></td><td>与 <code>useEffect</code> 类似，但在浏览器绘制之前同步执行</td></tr><tr><td><code>useDebugValue</code></td><td>在 React DevTools 中显示自定义 Hook 的标签</td></tr></tbody></table></section><section><h2>Hook 使用规则<a href="#hook-使用规则"><span>#</span></a></h2><ol>
<li><strong>只在最顶层调用 Hook</strong>
不要在循环、条件或嵌套函数中调用 Hook，确保每次渲染时 Hook 的调用顺序一致。</li>
<li><strong>只在 React 函数中调用 Hook</strong>
即在 React 函数组件或自定义 Hook 中调用，不要在普通 JavaScript 函数里使用。</li>
</ol><p>（React 官方提供了 ESLint 插件 <code>eslint-plugin-react-hooks</code> 来自动检测这些规则。）</p><section><h3>这是为什么<a href="#这是为什么"><span>#</span></a></h3><p>当你写一个函数组件并用 <code>useState</code> 时：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Counter</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> [</span><span>count</span><span>, </span><span>setCount</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>useState</code> 怎么知道这次渲染应该返回哪个状态？组件本身是一个普通函数，每次渲染都会重新执行，如果不在某个地方把状态“记住”，状态就会丢失。</p><p>React 的做法是：<strong>将 Hooks 的状态链表存储在组件对应的 Fiber 节点上</strong>（Fiber 是 React 内部代表组件实例的数据结构）。
每次组件渲染时，React 会根据当前正在渲染的 Fiber 节点，依次取出对应位置的 Hook 状态。</p><p>因此，<code>useState</code> 必须在 React 的渲染流程中被调用，这样 React 才能知道当前属于哪个组件实例，并把状态正确地取出或存入该实例对应的链表里。</p><p>如果在普通 JavaScript 函数里调用 Hook，React 就无法将其关联到任何 Fiber 节点，因为没有正在渲染的组件上下文，程序会直接报错。</p><p>当你在函数组件中多次调用 <code>useState</code> 或 <code>useEffect</code> 时，React 并没有在后台为你创建类似于 <code>{ countState: 0, nameState: 'Tom' }</code> 这样的键值对对象。相反，React 会将这些 Hook 按照它们在代码中<strong>书写的先后顺序</strong>，组装成一个<strong>单向链表（Linked List）</strong>。</p><p>当组件第一次渲染时，React 每解析到一个 Hook，就会在链表尾部追加一个节点：</p><blockquote><p>Fiber.memorizedState   —&gt; [ Hook 1 (useState: count) ] —&gt; [ Hook 2 (useState: name) ] —&gt; [ Hook 3 (useEffect) ]</p></blockquote><p>当组件因为状态改变而触发重新渲染（Re-render）时，函数组件会重新从头到尾执行一遍。</p><p>此时，React 的内部指针会重新回到这个链表的<strong>头部</strong>。每当执行到一行 Hook 代码，React 不会去问“这个状态叫什么名字”，而是直接取当前指针指向的链表节点的值。 随后，指针自动向后移动一步，指向下一个节点。</p><p>所以如果我们将 Hook 放在条件判断（如 <code>if</code> 语句）或循环中，就会导致<strong>某次渲染时，部分 Hook 被跳过</strong>。这会直接破坏链表的索引顺序，引发灾难性的状态串位。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>MyComponent</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> [</span><span>count</span><span>, </span><span>setCount</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);      </span><span>// Hook 1</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// 错误：在条件语句中调用 Hook</span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span><span> (</span><span>count</span><span> </span></span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>useEffect</span><span>(() </span><span>=&gt;</span><span> { </span><span>...</span><span> }, []);            </span><span>// Hook 2 (条件性触发)</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> [</span><span>name</span><span>, </span><span>setName</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>'Alice'</span><span>);  </span><span>// Hook 3</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>首次渲染（<code>count</code> 为 <code>0</code>）</p><p>由于 <code>count &gt; 0</code> 为 <code>false</code>，<code>if</code> 分支内部的 Hook 2 被跳过。React 构建的链表结构如下：</p><ul>
<li><strong>第 1 次调用：</strong> 对应链表第 1 个节点（存储 <code>count</code>）</li>
<li><strong>第 2 次调用：</strong> 对应链表第 2 个节点（存储 <code>name</code>）</li>
</ul><p><code>count</code> 变为了 <code>1</code>，组件触发更新</p><p>此时 <code>count &gt; 0</code> 为 <code>true</code>，<code>if</code> 分支内部的代码被执行。React 按照执行顺序去读取链表：</p><ul>
<li><strong>第 1 次调用 (<code>useState(0)</code>)：</strong> 读取链表第 1 个节点 ── 成功拿到 <code>count</code> 的最新值 <code>1</code>。</li>
<li><strong>第 2 次调用 (<code>useEffect(...)</code>)：</strong> 此时执行到了 <code>if</code> 内部的 Hook。React 盲目信任顺序，直接读取链表的<strong>第 2 个节点</strong>。然而，这个节点在上一次渲染时存储的是 <strong><code>name</code> (‘Alice’)</strong> 的数据！</li>
<li><strong>第 3 次调用 (<code>useState('Alice')</code>)：</strong> React 试图去读取链表的第 3 个节点，但上一次渲染根本没有生成第 3 个节点，此时内部指针直接指向 <code>null</code>，程序发生运行时崩溃（Runtime Error）。</li>
</ul><p>一旦前面的 Hook 被跳过，后续所有 Hook 的执行顺序与链表节点的对应关系就会全部错位。<strong>React 会把属于 A 状态的数据错误地赋值给 B 状态。</strong></p></section><section><h3>为什么 React 不用 Key 寻址<a href="#为什么-react-不用-key-寻址"><span>#</span></a></h3><p>为什么 React 不像 <code>Map</code> 一样，让开发者传一个唯一的 Key（例如 <code>useState('count', 0)</code>）来映射状态，这样不就不受顺序限制了吗？</p><ul>
<li>在大型项目或多人协作中，不同开发者在同一个组件或自定义 Hook 中可能会无意间使用相同的 Key，导致状态被覆盖。</li>
<li>Hooks 的终极目标是复用逻辑。如果采用 Key 机制，当你在同一个组件里多次调用同一个自定义 Hook 时（例如两次调用 <code>useFetch('/url')</code>），为了防止 Key 冲突，你必须手动为它们注入不同的 Key。</li>
</ul></section></section></section>
<section><h1>useState()<a href="#usestate"><span>#</span></a></h1><p><code>useState</code> 是 React 函数组件的状态钩子。它的核心职责是：在组件的多次“重渲染”（Re-render）生命周期之间，持久化（Persist）组件的内部状态，并提供一个触发视图更新的唯一入口。</p><p>在纯 JavaScript 中，普通局部变量会随着函数的执行结束而销毁，并在下一次函数执行时重新初始化。而 <code>useState</code> 能够突破函数作用域的限制，将状态托管给 React 底层的 Fiber 节点，从而让函数组件拥有了“记忆”能力。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>state</span><span>, </span><span>setState</span><span>] </span><span>=</span><span> </span><span>useState</span><span><span>(</span><span>initialState</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>state</code>（状态变量）： 这是一个只读（Read-Only）的快照数据。在当前渲染周期内，它是不可变的（Immutable）。</li>
<li><code>setState</code>（状态更新函数）： 更新状态的函数，调用时会触发组件重新渲染</li>
<li><code>initialState</code>（初始状态）： 仅在组件第一次挂载（Mount）时生效的默认值。可以是任何类型（数字、字符串、对象、数组，甚至是函数 —— 惰性初始化）</li>
</ul><section><h2>异步批量更新<a href="#异步批量更新"><span>#</span></a></h2><p><strong>调用 <code>setState</code> 不会立即改变当前执行流中的 <code>state</code> 变量。</strong> 它的本质是向 React 提交一个“更新请求”，并触发下一次渲染。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>count</span><span>, </span><span>setCount</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>handleClick</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>setCount</span><span><span>(</span><span>count</span><span> </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>count</span><span>); </span></span><span>// 打印出来的仍然是 0，而不是 1</span></div></div><div><div><div>6</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>每次渲染，<code>count</code> 都是一个特定时间点的“快照”。在 <code>handleClick</code> 函数被触发的那个渲染周期里，<code>count</code> 的值就是 <code>0</code>。改变状态会触发全新的下一次渲染，在下一次渲染的函数体中，<code>count</code> 才会变成 <code>1</code>。</p><p>同时 为了性能优化，React 会将同一个事件处理函数内的多个 <code>setState</code> 操作合并为一次重渲染，避免频繁触发 DOM 更新。</p></section><section><h2>函数式状态更新<a href="#函数式状态更新"><span>#</span></a></h2><p>当新的状态<strong>高度依赖于前一次的状态</strong>，或者在异步闭包（如 <code>setTimeout</code>）中需要获取最新状态时，应当向 <code>setState</code> 传入一个<strong>回调函数</strong>，而不是具体的值：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 假设 count 当前为 0</span></div></div><div><div><div>2</div></div><div><span>setCount</span><span><span>(</span><span>prevCount</span><span> </span></span><span>=&gt;</span><span><span> </span><span>prevCount</span><span> </span></span><span>+</span><span> </span><span>1</span><span>); </span><span>// 接收最新状态，返回新状态</span></div></div><div><div><div>3</div></div><div><span>setCount</span><span><span>(</span><span>prevCount</span><span> </span></span><span>=&gt;</span><span><span> </span><span>prevCount</span><span> </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 最终视图上 count 会变成 2。如果写成 setCount(count + 1)，最终只会是 1。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>惰性初始化<a href="#惰性初始化"><span>#</span></a></h2><p>如果初始状态需要经过复杂的计算（例如读取 localStorage、进行大量运算），可以直接传一个<strong>函数</strong>给 <code>useState</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>data</span><span>, </span><span>setData</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>saved</span><span> </span><span>=</span><span><span> </span><span>localStorage</span><span>.</span></span><span>getItem</span><span>(</span><span>'key'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span><span> </span><span>saved</span><span> </span></span><span>?</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>saved</span><span>) </span></span><span>:</span><span><span> </span><span>defaultValue</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这个函数<strong>只在组件首次渲染时执行一次</strong>，之后的渲染都会忽略它。如果直接写 <code>useState(func())</code>，则该表达式会在每次渲染时都执行（即使结果被丢弃），造成性能浪费。</p><p>当一个 <code>useState</code> 在 Mount 时被初始化，React 会在对应的 Hook 链表节点上挂载一个 <code>queue</code>（更新队列）对象。</p><p>每当你调用 <code>setState(x)</code> 时，React 并不会马上计算出最终结果，而是把 <code>x</code> 作为一个 <code>Update</code> 动作<strong>推入该 Hook 节点的更新队列中</strong>。当 React 调度器（Scheduler）开始处理该组件的重渲染时，会遍历这个队列，依次应用这些更新动作，最终计算出最新的状态值并付诸渲染。</p></section></section>
<section><h1>useEffect()<a href="#useeffect"><span>#</span></a></h1><p><code>useEffect</code> 是 React 函数组件的副作用（Side Effects）控制器。它的核心职责是：允许开发者在组件渲染流之外，执行声明式的、与外部系统同步的异步或命令式操作，并提供了一套完善的资源释放机制。</p><p><strong>副作用（Side Effect）</strong> 是指一个函数在计算其返回值的过程中，对外部世界产生的可观测的变化。在 React 中，纯粹的渲染函数应该是一个没有副作用的纯函数（Pure Function）。</p><p>诸如 <strong>API 数据请求、手动修改 DOM 树、设置定时器、记录日志、订阅外部数据源</strong> 等操作，都不属于组件渲染的核心逻辑。<code>useEffect</code> 的存在，正是为了将这些“副作用”从组件的渲染主线程中抽离出来，保证渲染流的纯净性与可预测性。</p><section><h2>语法<a href="#语法"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>useEffect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 1. 执行副作用逻辑（Effect）</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 2. 清理资源逻辑（Cleanup）</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>7</div></div><div><span><span>}, [</span><span>dependencies</span><span>]); </span></span><span>// 3. 依赖项数组（Dependency Array）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>依赖数组决定了副作用什么时候重新执行，React 调度器在每次重渲染后，会采用 <code>Object.is</code>比对当前渲染周期与上一次渲染周期的依赖项数组：</p><ul>
<li>省略依赖项（不传数组）： 副作用在 <strong>每次组件渲染完成（包括挂载与更新）后</strong> 都会执行。这通常会导致严重的性能问题或死循环。</li>
<li>空依赖项数组 <code>[]</code>：副作用仅在 <strong>组件首次挂载完成（Mount）时</strong> 执行一次。这意味着该副作用不依赖任何组件状态的变更。</li>
<li>非空依赖项数组 <code>[a, b]</code>： 在首次挂载时，以及后续任何一次渲染中，只要 <code>a</code> 或 <code>b</code> 的<strong>引用或值发生改变</strong>，副作用就会在渲染完成后重新触发。</li>
</ul><p>当依赖项改变触发组件重渲染时，React 的内部执行顺序如下：</p><ol>
<li>组件完成重渲染并更新 DOM。</li>
<li>执行上一次渲染所产生的副作用的清理函数。（擦除旧的副作用）</li>
<li>执行当前渲染所产生的新的副作用函数。（建立新的副作用）</li>
</ol></section><section><h2>清理函数<a href="#清理函数"><span>#</span></a></h2><p><code>useEffect</code> 允许返回一个函数作为清理器。清理器的执行并不是在组件销毁时才发生，React 会在两个时间点调用它：</p><ol>
<li>组件卸载时</li>
<li>下一次副作用执行前</li>
</ol><p>这是保证不产生内存泄漏的关键，比如清除定时器、取消订阅。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>useEffect</span><span>(</span></div></div><div><div><div>2</div></div><div><span>  </span><span>function</span><span> () {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>const</span><span> </span><span>timer</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>setCount</span><span><span>(</span><span>count</span><span> </span></span><span>=&gt;</span><span><span> </span><span>count</span><span> </span></span><span>+</span><span> </span><span>1</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}, </span><span>1000</span><span>)</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span> () </span><span>=&gt;</span><span> </span><span>clearInterval</span><span><span>(</span><span>timer</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>[</span><span>setCount</span><span>],</span></div></div><div><div><div>9</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果组件已卸载，但之前的异步操作或订阅仍然存在，会导致错误或内存泄漏。</p></section><section><h2>使用<a href="#使用"><span>#</span></a></h2><section><h3>依赖数组<a href="#依赖数组"><span>#</span></a></h3><p>Effect 里用到的所有组件内变量（props、state、派生值）都应该放到依赖数组中。React 通过 <code>Object.is</code> 比较依赖是否变化。不完整的依赖会导致 effect 拿到的变量是过时的闭包值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>useEffect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>timer</span><span> </span><span>=</span><span> </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>setCount</span><span><span>(</span><span>c</span><span> </span></span><span>=&gt;</span><span><span> </span><span>c</span><span> </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}, </span><span>2000</span><span>);</span></div></div><div><div><div>5</div></div><div><span>  </span><span>return</span><span> () </span><span>=&gt;</span><span> </span><span>clearTimeout</span><span><span>(</span><span>timer</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span>}, []); </span><span>// 依赖缺了 setCount</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>虽然 <code>[]</code> 和 <code>[setCount]</code> 都只是初始化时执行一次，相当于等价，但是 <code>eslint</code> 会警告。</p></section><section><h3>数据获取<a href="#数据获取"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>useEffect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>cancelled</span><span> </span></span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>async</span><span> </span><span>function</span><span> </span><span>fetchData</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>api</span><span>.</span></span><span>get</span><span><span>(</span><span>userId</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>cancelled</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>      </span><span>setData</span><span><span>(</span><span>result</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>  </span><span>fetchData</span><span>();</span></div></div><div><div><div>10</div></div><div><span>  </span><span>return</span><span> () </span><span>=&gt;</span><span><span> { </span><span>cancelled</span><span> </span></span><span>=</span><span> </span><span>true</span><span>; }; </span><span>// 防止组件卸载后设置状态</span></div></div><div><div><div>11</div></div><div><span><span>}, [</span><span>userId</span><span>]);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在异步网络请求中，较早发出的请求可能由于网络延迟，比稍后发出的请求更晚返回，导致卸载后执行 <code>setState</code> 覆盖想要的数据。</p><p><strong><code>useEffect</code> 获取数据时，为什么要额外创建一个 async 函数，而不能直接接收一个 async 函数？</strong></p><p>JavaScript 的 <code>async</code> 函数会隐式返回一个 Promise 对象，而 <code>useEffect</code> 的底层设计要求返回值必须是一个“纯粹的清理函数”或 <code>undefined</code>。两者的类型签名在底层发生了不可调和的冲突。</p><p><strong>为什么 <code>useEffect</code> 不支持接收 Promise？</strong></p><p>资源清理必须是同步且即时发生的。当依赖项改变触发重渲染时，React 必须立刻执行上一次的清理函数，以切断过期的副作用（如取消订阅、清除定时器）。 如果 <code>useEffect</code> 接收了 async 函数，清理函数就必须等到这个 Promise 被 resolve 之后才能被获取并执行。此时，外部系统可能已经进入了全新的状态，这种异步延迟的清理会导致内存泄漏。</p></section><section><h3>事件绑定<a href="#事件绑定"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>useEffect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>handleResize</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> </span><span>setWidth</span><span><span>(</span><span>window</span><span>.</span></span><span>innerWidth</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>window</span><span>.</span><span>addEventListener</span><span>(</span><span>'resize'</span><span><span>, </span><span>handleResize</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span> () </span><span>=&gt;</span><span><span> </span><span>window</span><span>.</span></span><span>removeEventListener</span><span>(</span><span>'resize'</span><span><span>, </span><span>handleResize</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span>}, []);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>组件初始化时，绑定 <code>resize</code> 事件，卸载组件解绑。</p></section></section></section>
<section><h1>useRef()<a href="#useref"><span>#</span></a></h1><p><code>useRef</code> 是 React 函数组件的“引用持有”钩子。它的核心职责是：在组件的整个生命周期内，跨越多次重渲染，持久化地持有同一个可变对象的引用，且该对象的属性变更<strong>不会触发组件的视图重渲染</strong>。</p><section><h2>语法<a href="#语法-1"><span>#</span></a></h2><p><code>useRef</code> 返回一个普通的 JavaScript 对象，该对象有一个 <code>current</code> 属性。<code>useRef</code> 返回的 <code>refContainer</code> 对象的内存地址，在组件的整个生命周期内是绝对不变的。无论组件因为状态改变重新渲染了多少次，React 始终返回最初 Mount 时创建的同一个对象。虽然外层的对象引用不可变，但其唯一的属性 <code>.current</code> 是完全可变的。你可以随时对其进行读写操作（如 <code>refContainer.current = newValue</code>）。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>refContainer</span><span> </span><span>=</span><span> </span><span>useRef</span><span><span>(</span><span>initialValue</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>initialValue</code>：<code>current</code> 属性的初始值，可以是任何类型。</li>
<li><code>refContainer</code> 在每次渲染时都是<strong>同一个对象引用</strong>。</li>
</ul><p><code>useRef</code> 同样会在组件的 Hook 链表上创建一个节点，保存 <code>{ current: initialValue }</code> 这个对象。因为每次渲染返回的都是同一个内存对象，所以修改 <code>ref.current</code> 不会导致节点本身发生变化，React 自然也不会去触发重渲染。</p></section><section><h2>用法<a href="#用法"><span>#</span></a></h2><section><h3>访问 DOM 元素<a href="#访问-dom-元素"><span>#</span></a></h3><p>最常见的场景就是获取某个 DOM 元素的直接引用，进行聚焦、滚动、测量等操作。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>TextInputWithFocusButton</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>inputRef</span><span> </span><span>=</span><span> </span><span>useRef</span><span>(</span><span>null</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>focusInput</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 直接访问 DOM 元素并调用 focus 方法</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>inputRef</span><span>.</span><span>current</span><span>.</span><span>focus</span><span>();</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;</span><span>input</span><span> </span><span>ref</span><span><span>=</span><span>{</span><span>inputRef</span><span>}</span><span> </span></span><span>type</span><span>=</span><span>"text"</span><span> /&gt;</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>focusInput</span><span>}</span><span>&gt;聚焦输入框&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ul>
<li>通过 <code>ref</code> 属性将 <code>inputRef</code> 绑定到 <code>&lt;input&gt;</code> 元素上。</li>
<li>渲染完成后，<code>inputRef.current</code> 就指向那个真实的 DOM 节点。</li>
</ul></section><section><h3>保存任何可变值<a href="#保存任何可变值"><span>#</span></a></h3><p>你可以把 <code>useRef</code> 当成一个在多次渲染之间共享的“储物柜”，存放那些希望保持最新、但又不想触发渲染的值，例如：</p><ul>
<li>定时器 ID</li>
<li>之前的状态（prevProps/prevState）</li>
<li>一个不需要响应式更新的数据副本</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Timer</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>intervalRef</span><span> </span><span>=</span><span> </span><span>useRef</span><span>(</span><span>null</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> [</span><span>count</span><span>, </span><span>setCount</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>start</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>// 把定时器 ID 存在 ref 里，清理时用</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>intervalRef</span><span>.</span><span>current</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>      </span><span>setCount</span><span><span>(</span><span>c</span><span> </span></span><span>=&gt;</span><span><span> </span><span>c</span><span> </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}, </span><span>1000</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> </span><span>stop</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>clearInterval</span><span><span>(</span><span>intervalRef</span><span>.</span></span><span>current</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>// 组件卸载时清理定时器</span></div></div><div><div><div>17</div></div><div><span>  </span><span>useEffect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> () </span><span>=&gt;</span><span> </span><span>clearInterval</span><span><span>(</span><span>intervalRef</span><span>.</span></span><span>current</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}, []);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>&lt;</span><span>p</span><span>&gt;</span><span>{</span><span>count</span><span>}</span><span>&lt;/</span><span>p</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>start</span><span>}</span><span>&gt;开始&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>stop</span><span>}</span><span>&gt;停止&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这里 <code>intervalRef.current</code> 存储定时器 ID，更新 <code>count</code> 会重渲染，但 <code>intervalRef</code> 中的值保持不变。</p></section></section><section><h2>与 useState() 的区别<a href="#与-usestate-的区别"><span>#</span></a></h2>

<table><thead><tr><th></th><th>useState</th><th>useRef</th></tr></thead><tbody><tr><td>触发重新渲染</td><td>是</td><td>否</td></tr><tr><td>值的更新</td><td>异步、不可变（替换）</td><td>同步、可变（直接修改 <code>.current</code>）</td></tr><tr><td>作用</td><td>驱动 UI 变化的数据</td><td>不需要体现在 UI 上的持久数据、DOM 引用</td></tr><tr><td>每次渲染</td><td>返回当前状态快照</td><td>返回同一个对象引用</td></tr></tbody></table><p>判断数据是否应放在 ref 还是 state 的核心标准是：该数据变化时是否需要重新渲染界面。</p><p>React 的官方文档明确指出：<strong>不要在渲染期间读取或写入 ref.current</strong>（除了初始化）。组件的渲染过程可能会<strong>被中断然后重新执行</strong>。如果渲染时修改了 ref，那么这种修改可能会被多次应用，产生不可预测的结果。 一般在 useEffect / useLayoutEffect 内部，或者在 <strong>事件处理函数</strong> 内部对 <code>.current</code> 进行读写。</p></section></section>
<section><h1>useReducer()<a href="#usereducer"><span>#</span></a></h1><p><code>useReducer</code> 是 <code>useState</code> 的一种替代方案，它借鉴了 <strong>Redux</strong> 的 reducer 模式，用于管理<strong>包含多个子值、或者下一个状态依赖前一个状态</strong>的复杂状态。</p><p>它的核心思想是：把状态更新的逻辑从组件的事件处理函数中，**集中收敛到一个外部的纯函数（reducer）**中。</p><section><h2>基本语法<a href="#基本语法"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>state</span><span>, </span><span>dispatch</span><span>] </span><span>=</span><span> </span><span>useReducer</span><span><span>(</span><span>reducer</span><span>, </span><span>initialState</span><span>, </span><span>init</span></span><span>?</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>state</code>：当前状态。通常是一个复杂的 JavaScript 对象（例如包含表单数据、加载状态、错误信息的复合体）。与 <code>useState</code> 一样，这里的状态同样不可变</li>
<li><code>dispatch</code>：一个函数，它是组件触发状态变更的<strong>唯一方式</strong>。调用它并传入 action 对象，如 <code>dispatch({ type: 'increment' })</code>，React 会将当前 state 和该 action 交给 reducer，用返回值作为新状态，并触发重渲染。</li>
<li><code>reducer</code>：形如 <code>(state, action) =&gt; newState</code> 的纯函数，负责根据 action 计算出新状态。<code>action</code> 通常包含两个标准属性：<code>type</code>：字符串常量，表示行为的唯一类型（如 <code>'FETCH_START'</code>）。<code>payload</code>：附带的业务数据负载（如 API 返回的数据包）。</li>
<li><code>initialState</code>：初始状态值 。</li>
<li><code>init</code>（可选）：惰性初始化函数，签名 <code>(initialArg) =&gt; initialState</code>，用于从传入的参数中计算初始状态。</li>
</ul><p>当遇到以下情况时，<code>useReducer</code> 会比 <code>useState</code> 更合适：</p><ol>
<li>
<p><strong>状态之间有复杂的依赖关系</strong></p>
<p>例如表单有多个字段互相影响，使用多个 <code>useState</code> 容易产生多个分散的 <code>setXxx</code> 调用，且不易追踪整体逻辑。</p>
</li>
<li>
<p><strong>下一个状态强依赖前一个状态</strong></p>
<p>在 <code>useState</code> 中必须用函数式更新 <code>setState(prev =&gt; ...)</code>，但若更新逻辑很复杂（如根据前一个状态的多个属性计算新状态），函数式更新会变得冗长。<code>useReducer</code> 直接在 reducer 函数中拿到最新的 <code>state</code>，编写逻辑更自然。</p>
</li>
<li>
<p><strong>需要将 dispatch 传递给深层子组件，避免回调重新创建</strong></p>
<p>React 保证 <code>dispatch</code> 函数在组件生命周期内引用稳定（和 <code>useState</code> 的 setState 一样），所以它不用写进 <code>useCallback</code> 的依赖中，可安全传给子组件。</p>
</li>
</ol></section><section><h2>计数器示例<a href="#计数器示例"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>React</span><span>, { </span><span>useReducer</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 定义初始状态</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>initialState</span><span> </span><span>=</span><span> { </span><span>count</span><span>:</span><span> </span><span>0</span><span>, </span><span>error</span><span>:</span><span> </span><span>null</span><span> };</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 编写 Reducer 纯函数</span></div></div><div><div><div>7</div></div><div><span>function</span><span> </span><span>counterReducer</span><span><span>(</span><span>state</span><span>, </span><span>action</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>  </span><span>switch</span><span><span> (</span><span>action</span><span>.</span></span><span>type</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>case</span><span> </span><span>'INCREMENT'</span><span>:</span></div></div><div><div><div>10</div></div><div><span>      </span><span>return</span><span> { </span><span>...</span><span><span>state</span><span>, </span></span><span>count</span><span>:</span><span><span> </span><span>state</span><span>.</span></span><span>count</span><span> </span><span>+</span><span> </span><span>1</span><span> };</span></div></div><div><div><div>11</div></div><div><span>    </span><span>case</span><span> </span><span>'DECREMENT'</span><span>:</span></div></div><div><div><div>12</div></div><div><span>      </span><span>return</span><span> { </span><span>...</span><span><span>state</span><span>, </span></span><span>count</span><span>:</span><span><span> </span><span>Math</span><span>.</span></span><span>max</span><span>(</span><span>0</span><span><span>, </span><span>state</span><span>.</span></span><span>count</span><span> </span><span>-</span><span> </span><span>1</span><span>) };</span></div></div><div><div><div>13</div></div><div><span>    </span><span>case</span><span> </span><span>'SET_ERROR'</span><span>:</span></div></div><div><div><div>14</div></div><div><span>      </span><span>return</span><span> { </span><span>...</span><span><span>state</span><span>, </span></span><span>error</span><span>:</span><span><span> </span><span>action</span><span>.</span></span><span>payload</span><span> };</span></div></div><div><div><div>15</div></div><div><span>    </span><span>case</span><span> </span><span>'RESET'</span><span>:</span></div></div><div><div><div>16</div></div><div><span>      </span><span>return</span><span><span> </span><span>initialState</span><span>;</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>default</span><span>:</span></div></div><div><div><div>18</div></div><div><span>      </span><span>// 遇到未定义的 Action 时抛出错误或返回原状态</span></div></div><div><div><div>19</div></div><div><span>      </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>`Unhandled action type: </span><span>${</span><span>action</span><span>.</span><span>type</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 组件内部集成</span></div></div><div><div><div>24</div></div><div><span>function</span><span> </span><span>CounterComponent</span><span>() {</span></div></div><div><div><div>25</div></div><div><span>  </span><span>const</span><span> [</span><span>state</span><span>, </span><span>dispatch</span><span>] </span><span>=</span><span> </span><span>useReducer</span><span><span>(</span><span>counterReducer</span><span>, </span><span>initialState</span><span>);</span></span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>29</div></div><div><span><span>      </span></span><span>&lt;</span><span>p</span><span>&gt;当前计数: </span><span>{</span><span><span>state</span><span>.</span></span><span>count</span><span>}</span><span>&lt;/</span><span>p</span><span>&gt;</span></div></div><div><div><div>30</div></div><div><span>      </span><span>{</span><span><span>state</span><span>.</span></span><span>error</span><span> </span><span>&amp;&amp;</span><span> &lt;</span><span>p</span><span> </span><span>style</span><span><span>=</span><span>{</span><span>{ </span></span><span>color</span><span>:</span><span> </span><span>'red'</span><span><span> }</span><span>}</span><span>&gt;</span><span>{</span><span>state</span><span>.</span></span><span>error</span><span><span>}</span><span>&lt;/</span></span><span>p</span><span>&gt;</span><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>      </span><span>{</span><span>/* 组件不直接修改状态，只通过 dispatch 宣告行为 */</span><span>}</span></div></div><div><div><div>33</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>dispatch</span><span>({ </span><span>type</span><span>:</span><span> </span><span>'INCREMENT'</span><span><span> })</span><span>}</span><span>&gt;增加&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>34</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>dispatch</span><span>({ </span><span>type</span><span>:</span><span> </span><span>'DECREMENT'</span><span><span> })</span><span>}</span><span>&gt;减少&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>35</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>dispatch</span><span>({ </span><span>type</span><span>:</span><span> </span><span>'SET_ERROR'</span><span>, </span><span>payload</span><span>:</span><span> </span><span>'触发一次模拟错误'</span><span><span> })</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>报错</span></div></div><div><div><div>37</div></div><div><span><span>      </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>更新逻辑全部在 <code>reducer</code> 里，组件本身只是通过 <code>dispatch</code> 发“命令”（action）。</p></section><section><h2>惰性初始化<a href="#惰性初始化-1"><span>#</span></a></h2><p>如果初始状态需要从 props 或其他数据中计算得出，你可以传入第三个参数 <code>init</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>init</span><span><span>(</span><span>initialCount</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> { </span><span>count</span><span>:</span><span><span> </span><span>initialCount</span><span> };</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>Counter</span><span><span>({ </span><span>initialCount</span><span> }) {</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> [</span><span>state</span><span>, </span><span>dispatch</span><span>] </span><span>=</span><span> </span><span>useReducer</span><span><span>(</span><span>reducer</span><span>, </span><span>initialCount</span><span>, </span><span>init</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>init()</code> 接收当前的初始状态值，并将返回值作为最新的状态值返回给 <code>state</code>，<code>init(initialCount)</code> 只会在首次渲染时执行。</p></section><section><h2>与 useState() 区别<a href="#与-usestate-区别"><span>#</span></a></h2>

<table><thead><tr><th></th><th>useState</th><th>useReducer</th></tr></thead><tbody><tr><td>适用场景</td><td>简单独立的状态值</td><td>复杂关联的状态、多个操作类型</td></tr><tr><td>更新方式</td><td>直接设置新值或函数式更新</td><td>派发 action，由 reducer 决定如何变化</td></tr><tr><td>逻辑位置</td><td>分散在事件处理函数中</td><td>集中在 reducer 纯函数中</td></tr><tr><td>下一个状态依赖前一个</td><td>必须使用函数式更新 <code>prev =&gt; ...</code></td><td>在 reducer 中直接接收最新 state</td></tr></tbody></table><ul>
<li>如果状态更新有<strong>多种操作类型</strong>（如增、删、改、重置），<code>useReducer</code> 更清晰。</li>
<li>如果只是简单的 toggle 或文本输入，<code>useState</code> 完全足够。</li>
</ul><blockquote><p>注：</p><ol>
<li>
<p><strong>reducer 必须是纯函数</strong></p>
<p>不能在里面执行副作用（如网络请求、修改 DOM），副作用应放在 useEffect 中。</p>
</li>
<li>
<p><strong>action 通常是一个对象</strong> { type, payload? }</p>
<p>这是约定俗成的惯例，但非强制。也可以自行定义 action 的结构，只要 reducer 能识别。</p>
</li>
<li>
<p><strong>dispatch 是稳定的引用</strong></p>
<p>与 useState 的 setState 一样，dispatch 不会在重渲染时变化，所以可以安全地传给子组件而无需 useCallback。</p>
</li>
<li>
<p><strong>状态更新是批处理的</strong></p>
<p>同 useState，多次 dispatch 在事件处理函数中会被批处理，React 会等到事件处理结束再统一应用所有更新。</p>
</li>
<li>
<p><strong>遵循 Hook 链表顺序</strong></p>
<p>useReducer 同样在组件的 Hook 链表中占据一个节点，按顺序存储状态。调用 dispatch 时，React 会标记更新，重渲染时按顺序读出状态，而不会重新调用 reducer 来计算当前状态（reducer 只在 dispatch 时运行）。</p>
</li>
</ol></blockquote></section></section>
<section><h1>useContext()<a href="#usecontext"><span>#</span></a></h1><p><code>useContext</code> 是 React 用于订阅和感知 React Context 变更的状态钩子。当我们需要跨越多个组件层级传递数据时，如果仍然通过 <code>props</code> 一层层传递（即“props 钻孔”），代码会变得繁琐且难以维护。</p><p>它的核心职责是：打破 React 传统的“单向数据流”中必须逐层传递 Props 的物理限制，允许深层子组件以声明式的方式，直接跳跃层级，订阅并获取祖先组件下发的全局或共享上下文数据。能直接在任意深度的子组件中读取到来自上层的数据。</p><ul>
<li>
<p>Provider 值变化会触发所有消费者重新渲染</p>
<p>当 <code>&lt;Context.Provider&gt;</code> 的 <code>value</code> 属性发生改变时，所有调用了 <code>useContext(Context)</code> 的深层子组件都会被强制触发重新渲染（不管是否真的用到了变化部分）。</p>
</li>
</ul><section><h2>基本语法<a href="#基本语法-1"><span>#</span></a></h2><section><h3>1. 创建 Context<a href="#1-创建-context"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>createContext</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>ThemeContext</span><span> </span><span>=</span><span> </span><span>createContext</span><span>(</span><span>'light'</span><span>); </span><span>// 参数为默认值</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2. 在上层提供数据（Provider）<a href="#2-在上层提供数据provider"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>ThemeContext</span><span> } </span><span>from</span><span> </span><span>'./ThemeContext'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>function</span><span> </span><span>App</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>theme</span><span>, </span><span>setTheme</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>'dark'</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>ThemeContext.Provider</span><span> </span><span>value</span><span><span>=</span><span>{</span><span>{ </span><span>theme</span><span>, </span><span>setTheme</span><span> }</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;</span><span>Toolbar</span><span> /&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/</span><span>ThemeContext.Provider</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在组件树的某一个上级节点，使用 <code>&lt;ThemeContext.Provider&gt;</code> 包裹子组件，并通过 <code>value</code> 属性将实时状态广播出去。</p></section><section><h3>3. 在任何子组件中消费（useContext）<a href="#3-在任何子组件中消费usecontext"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useContext</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>ThemeContext</span><span> } </span><span>from</span><span> </span><span>'./ThemeContext'</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>function</span><span> </span><span>Toolbar</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// 直接拿到 App 中提供的 theme 和 setTheme</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> { </span><span>theme</span><span>, </span><span>setTheme</span><span> } </span><span>=</span><span> </span><span>useContext</span><span><span>(</span><span>ThemeContext</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>style</span><span><span>=</span><span>{</span><span>{ </span></span><span>background</span><span>:</span><span><span> </span><span>theme</span><span> </span><span>===</span><span> </span></span><span>'dark'</span><span><span> </span><span>?</span><span> </span></span><span>'#333'</span><span><span> </span><span>:</span><span> </span></span><span>'#fff'</span><span><span> }</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>setTheme</span><span><span>(</span><span>theme</span><span> </span><span>===</span><span> </span></span><span>'dark'</span><span><span> </span><span>?</span><span> </span></span><span>'light'</span><span><span> </span><span>:</span><span> </span></span><span>'dark'</span><span><span>)</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>切换主题</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>Toolbar</code> 内部不需要从父组件接收任何 <code>props</code>，只需引入上下文对象，传入 <code>useContext</code> 钩子，就能拿到全局主题并更新它。</p></section></section><section><h2>与 <code>useReducer</code> 配合实现全局状态管理<a href="#与-usereducer-配合实现全局状态管理"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>AppContext</span><span> </span><span>=</span><span> </span><span>createContext</span><span>();</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>function</span><span> </span><span>reducer</span><span><span>(</span><span>state</span><span>, </span><span>action</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>switch</span><span><span> (</span><span>action</span><span>.</span></span><span>type</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>case</span><span> </span><span>'login'</span><span>: </span><span>return</span><span> { </span><span>...</span><span><span>state</span><span>, </span></span><span>user</span><span>:</span><span><span> </span><span>action</span><span>.</span></span><span>payload</span><span> };</span></div></div><div><div><div>6</div></div><div><span>    </span><span>case</span><span> </span><span>'logout'</span><span>: </span><span>return</span><span> { </span><span>...</span><span><span>state</span><span>, </span></span><span>user</span><span>:</span><span> </span><span>null</span><span> };</span></div></div><div><div><div>7</div></div><div><span>    </span><span>default</span><span>: </span><span>return</span><span><span> </span><span>state</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>AppProvider</span><span><span>({ </span><span>children</span><span> }) {</span></span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> [</span><span>state</span><span>, </span><span>dispatch</span><span>] </span><span>=</span><span> </span><span>useReducer</span><span><span>(</span><span>reducer</span><span>, { </span></span><span>user</span><span>:</span><span> </span><span>null</span><span> });</span></div></div><div><div><div>13</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;</span><span>AppContext.Provider</span><span> </span><span>value</span><span><span>=</span><span>{</span><span>state</span><span>, </span><span>dispatch</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>{</span><span>children</span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>&lt;/</span><span>AppContext.Provider</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 自定义 Hook，封装 useContext 调用</span></div></div><div><div><div>21</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>useApp</span><span>() {</span></div></div><div><div><div>22</div></div><div><span>  </span><span>const</span><span> </span><span>context</span><span> </span><span>=</span><span> </span><span>useContext</span><span><span>(</span><span>AppContext</span><span>);</span></span></div></div><div><div><div>23</div></div><div><span>  </span><span>if</span><span><span> (</span><span>context</span><span> </span></span><span>===</span><span><span> </span><span>undifined</span><span>) </span></span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'AppContext must used AppProvider'</span><span>);</span></div></div><div><div><div>24</div></div><div><span>  </span><span>return</span><span><span> </span><span>context</span><span>;</span></span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>任何在 <code>&lt;AppProvider&gt;&lt;/AppProvider&gt;</code> 包裹的子组件都可以调用 <code>useApp()</code> 获取 <code>state</code> 和 <code>dispatch</code> 。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/guan-yu-cesium-zai-micro-app-xia-wu-fa-xian-shi-de-wen-ti/</id>
      <title type="text">关于 cesium 在 micro-app 下无法显示的问题</title>
      <published>2026-03-25T06:17:07.000Z</published>
      <updated>2026-03-25T06:17:07.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/guan-yu-cesium-zai-micro-app-xia-wu-fa-xian-shi-de-wen-ti/"/>
      <summary type="text">基座：vite + vue3 子应用：vite + vue3 micro app v1.0.0 rc.29, cesium v1.135 子应用包含一个 cesium map，在基座打开子应用时，cesium 无法显示地图，但是 sence, sky box, model 都能正常显示，唯独地图不显…</summary>
      <content type="html"><![CDATA[<p>基座：vite + vue3</p>
<p>子应用：vite + vue3</p>
<p><a href="https://jd-opensource.github.io/micro-app/0.x/docs.html" target="_blank">micro-app</a> v1.0.0-rc.29, cesium v1.135</p>
<p>子应用包含一个 cesium map，在基座打开子应用时，cesium 无法显示地图，但是 sence, sky box, model 都能正常显示，唯独地图不显示。而单独运行子程序时，一切都能正常显示。</p>
<section><h2>解决过程<a href="#解决过程"><span>#</span></a></h2><p>最初的猜想是静态资源路径解析错误，导致 <code>Assets</code>、<code>Workers</code>、<code>Widgets</code> 静态资源未能成功加载上。</p><p>故设置了 <code>CESIUM_BASE_URL</code> :</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 在引入 Cesium 或者创建 Viewer 之前执行</span></div></div><div><div><div>2</div></div><div><span>if</span><span><span> (</span><span>window</span><span>.</span></span><span>__MICRO_APP_ENVIRONMENT__</span><span>) {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// __MICRO_APP_PUBLIC_PATH__ 是 micro-app 注入的子应用绝对路径（带有子应用端口号）</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// 假设 cesium 静态资源放在子应用的 public/cesium 目录下</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>window</span><span>.</span><span>CESIUM_BASE_URL</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>__MICRO_APP_PUBLIC_PATH__</span><span> </span><span>+</span><span> </span><span>'cesium/'</span><span>;</span></div></div><div><div><div>6</div></div><div><span>} </span><span>else</span><span> {</span></div></div><div><div><div>7</div></div><div><span>  </span><span>// 独立运行时的路径</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>window</span><span>.</span><span>CESIUM_BASE_URL</span><span> </span><span>=</span><span> </span><span>'/cesium/'</span><span>;</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是无论设不设置，<code>cesium.js</code> <code>widgets.css</code> 都是能够正常访问的。
</p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2026/03/image-20260320160217611_1773993740.png" alt="image-20260320160217611" /><figcaption>image-20260320160217611</figcaption></figure><p></p><p>AI 提出 Cesium 极其依赖 Web Worker 来处理几何数据。在微前端中，基座和子应用往往运行在不同的端口（跨域）。浏览器出于安全策略，不允许跨域直接加载 Web Worker 脚本。但是 console 并没有报错跨域的问题，我看到 micro-app 可以设置 <code>iframe</code> 模式，实现了隔离，便认为 iframe 拦截了 Web Worker 脚本，导致地图不加载，所以按照 iframe 的方式设置了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>micro-app</span></div></div><div><div><div>2</div></div><div><span>  </span><span>name</span><span>=</span><span>"cesium-map"</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>:</span><span>url</span><span>=</span><span>"</span><span>url</span><span>"</span></div></div><div><div><div>4</div></div><div><span>  </span><span>iframe</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>@</span><span>created</span><span>=</span><span>"</span><span>handleCreated</span><span>"</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>@</span><span>beforemount</span><span>=</span><span>"</span><span>handleBeforeMount</span><span>"</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>@</span><span>mounted</span><span>=</span><span>"</span><span>handleMounted</span><span>"</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>@</span><span>unmount</span><span>=</span><span>"</span><span>handleUnmount</span><span>"</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>@</span><span>error</span><span>=</span><span>"</span><span>handleError</span><span>"</span></div></div><div><div><div>10</div></div><div><span>  </span><span>router-mode</span><span>=</span><span>"native"</span></div></div><div><div><div>11</div></div><div><span>  </span><span>sandbox</span><span>=</span><span>"allow-scripts allow-same-origin allow-forms allow-popups"</span></div></div><div><div><div>12</div></div><div><span>  </span><span>allow</span><span>=</span><span>"cross-origin-isolated; geolocation"</span></div></div><div><div><div>13</div></div><div><span>  </span><span>baseroute</span><span>=</span><span>"/"</span><span>&gt;&lt;/</span><span>micro-app</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>结果也不对，在 iframe 中确实能够正常运行，micro-app 并不支持，后来了解到 micro-app 不是通过 iframe 实现的，是借鉴了 WebComponent 的思想，通过CustomElement 将 micro-app 封装成一个类 WebComponent 组件。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;</span><span>iframe</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>:</span><span>src</span><span>=</span><span>"</span><span>src</span><span>"</span></div></div><div><div><div>3</div></div><div><span>    </span><span>frameborder</span><span>=</span><span>"0"</span></div></div><div><div><div>4</div></div><div><span>    </span><span>class</span><span>=</span><span>"iframe"</span></div></div><div><div><div>5</div></div><div><span>    </span><span>sandbox</span><span>=</span><span>"allow-scripts allow-same-origin allow-forms allow-popups"</span></div></div><div><div><div>6</div></div><div><span>    </span><span>allow</span><span>=</span><span>"cross-origin-isolated; geolocation"</span><span>&gt;&lt;/</span><span>iframe</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在翻阅官方 github issues 时，发现了相同的问题 <a href="https://github.com/jd-opensource/micro-app/issues/1556" target="_blank">Vite项目 Cesium地图不展示 到底是什么原因</a>，下面有初始化 microApp 的配置：</p><p>参考 <a href="https://github.com/jd-opensource/micro-app-awesome-samples/blob/issue-1351/apps/main-app/src/main.js" target="_blank">https://github.com/jd-opensource/micro-app-awesome-samples/blob/issue-1351/apps/main-app/src/main.js</a></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>microApp</span><span>.</span></span><span>start</span><span>({</span></div></div><div><div><div>2</div></div><div><span>    </span><span>excludeAssetFilter</span><span><span> (</span><span>assetUrl</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>if</span><span><span> (</span><span>assetUrl</span><span> </span></span><span>===</span><span> </span><span>'createVerticesFromHeightmap'</span><span> </span><span>||</span><span><span> </span><span>assetUrl</span><span> </span></span><span>===</span><span> </span><span>'transferTypedArrayTest'</span><span> </span><span>||</span><span><span> </span><span>assetUrl</span><span> </span></span><span>===</span><span> </span><span>'createVerticesFromQuantizedTerrainMesh'</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>          </span><span>return</span><span> </span><span>true</span><span> </span><span>// 返回true则micro-app不会劫持处理当前文件</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>        </span><span>return</span><span> </span><span>false</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span>    </span><span>plugins</span><span>:</span><span> {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>modules</span><span>:</span><span> {</span></div></div><div><div><div>10</div></div><div><span>            </span><span>'cesium-map'</span><span>:</span><span> [{</span></div></div><div><div><div>11</div></div><div><span>                </span><span>loader</span><span><span>(</span><span>code</span><span>, </span><span>url</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>                    </span><span>// TODO 根据 实际情况再调整限制</span></div></div><div><div><div>13</div></div><div><span>                    </span><span>if</span><span><span>(</span><span>url</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Cesium.js"</span><span>) </span><span>&gt;</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>code</span><span>.</span></span><span>indexOf</span><span>(</span><span>'var Cesium='</span><span>)</span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>14</div></div><div><span><span>                        </span></span><span>code</span><span> </span><span>=</span><span><span> </span><span>code</span><span>.</span></span><span>replace</span><span>(</span><span>'var Cesium='</span><span>, </span><span>'window.Cesium='</span><span>)</span></div></div><div><div><div>15</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>                    </span><span>return</span><span><span> </span><span>code</span></span></div></div><div><div><div>17</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>}]</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>excludeAssetFilter(assetUrl) { ... }</code></p><p>字面意思是静态资源过滤器。根据官方解释，如果这个函数返回 <code>true</code>，micro-app 就不会去劫持和处理这个资源的加载请求，而是让浏览器按原生方式去加载。</p><p>而代码中拦截的这些名称（<code>createVerticesFromHeightmap</code>、<code>createGeometry</code> 等），<strong>是 Cesium 内部的 Web Workers 任务脚本</strong>。 Cesium 为了保证 3D 渲染的流畅，把海量的几何体计算、地形切片计算都放在了后台 Web Worker 线程中。如果 micro-app 去劫持这些 Worker 脚本，会导致这些 Worker 脚本运行不了，这也是为什么 sence 以及引入的 model 都能够成功加载，而地形layer的加载依赖 createVerticesFromHeightmap.js 脚本，所以才会造成这个原因。</p><p>设置了 <code>excludeAssetFilter</code> 本地确实能够正常运行了，但是在我部署到线上的时候，console 报错：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>未找到对象 ReferenceError: Cesium is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>plugins: { modules: { 'cesium-map': [ ... ] } }</code></p><p>官方指出 <a href="https://jd-opensource.github.io/micro-app/0.x/docs.html#/zh-cn/sandbox" target="_blank">https://jd-opensource.github.io/micro-app/0.x/docs.html#/zh-cn/sandbox</a></p><blockquote><p>我们使用<code>Proxy</code>拦截了用户全局操作的行为，防止对window的访问和修改，避免全局变量污染。<code>micro-app</code>中的每个子应用都运行在沙箱环境，以获取相对纯净的运行空间。</p><p>沙箱是默认开启的，正常情况下不建议关闭，以避免出现不可预知的问题。</p></blockquote><p>在沙箱环境中，顶层变量不会泄漏为全局变量。</p><p>例如在正常情况下，通过 <code>var name</code>  或 <code>function name () {}</code> 定义的顶层变量会泄漏为全局变量，通过<code>window.name</code>或<code>name</code>就可以全局访问。</p><p>但是在沙箱环境下这些顶层变量无法泄漏为全局变量，<code>window.name</code>或<code>name</code>的值为<code>undefined</code>，导致出现问题。</p><p>而 <code>Cesium.js</code> 开头：<code>var Cesium=(()=&gt;{...</code>，是将 <code>Cesium</code> 提升到了全局变量 <code>window.Cesium</code>，结果时不能在基座中访问，导致初始化时 <code>ReferenceError: Cesium is not defined</code>：</p><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2026/03/image-20260320172255961_1773998577.png" alt="image-20260320172255961" /><figcaption>image-20260320172255961</figcaption></figure><p></p><p>在 js 沙箱，可以通过将 <code>var name</code> 改为 <code>window.name</code> 挂载在全局 <code>window</code> 上，因为我们不能直接修改 <code>Cesium.js</code> 文件，官方提供了类似 webpack 打包时 loader 修改代码，通过匹配 <code>Cesium.js</code> 文件名，将开头的 <code>var Cesium=</code> 替换为 <code>window.Cesium=</code> 。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>loader</span><span><span>(</span><span>code</span><span>, </span><span>url</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span><span> (</span><span>url</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Cesium.js"</span><span>) </span><span>&gt;</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>code</span><span>.</span></span><span>indexOf</span><span>(</span><span>'var Cesium='</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>code</span><span> </span><span>=</span><span><span> </span><span>code</span><span>.</span></span><span>replace</span><span>(</span><span>'var Cesium='</span><span>, </span><span>'window.Cesium='</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span><span> </span><span>code</span></span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>到目前为止问题已经解决，但是我们从哪儿获取到<code>excludeAssetFilter</code>中需要放行的<code>worker</code>脚本呢？</p><p>在 <code>\node_modules\cesium\Build\Cesium\Workers</code> 下，能发现许多js文件：</p><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2026/03/image-20260320173654778_1773999416.png" alt="image-20260320173654778" /><figcaption>image-20260320173654778</figcaption></figure><p></p><p>以代码中为例，<code>createVerticesFromHeightmap.js</code> 是从高度图（heightmap）生成三维地形的顶点数据，而 micro-app 将其拦截刚好对应了地形没有显示的问题。如果需要绘制 <code>geometry</code> 就需要将 <code>Workers</code> 下的 <code>createGeometry.js</code> 加入 <code>loader</code> 。</p><p>以上。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/typescript-jin-jie/</id>
      <title type="text">TypeScript 进阶</title>
      <published>2026-03-25T06:15:48.000Z</published>
      <updated>2026-03-25T06:15:48.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/typescript-jin-jie/"/>
      <summary type="text">数组合并了相同类型的对象，而元组（Tuple）合并了不同类型的对象。 元组起源于函数编程语言（如 F ），这些语言中会频繁使用元组。 元组是⼀种特殊的数组类型，可以存储固定数量的元素，并且每个元素的类型是已知的且可以不同。元组⽤于精确描述⼀组值的类型， ? 表示可选元素。 定义一对值分别为 stri…</summary>
      <content type="html"><![CDATA[<section><h1>元组<a href="#元组"><span>#</span></a></h1><p>数组合并了相同类型的对象，而元组（Tuple）合并了不同类型的对象。</p><p>元组起源于函数编程语言（如 F#），这些语言中会频繁使用元组。</p><p>元组是⼀种特殊的数组类型，可以存储固定数量的元素，并且每个元素的类型是已知的且可以不同。元组⽤于精确描述⼀组值的类型， ? 表示可选元素。</p><section><h2>简单的例子<a href="#简单的例子"><span>#</span></a></h2><p>定义一对值分别为 <code>string</code> 和 <code>number</code> 的元组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> [</span><span>string</span><span>, </span><span>number</span><span>] </span><span>=</span><span> [</span><span>'Tom'</span><span>, </span><span>25</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当赋值或访问一个已知索引的元素时，会得到正确的类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> [</span><span>string</span><span>, </span><span>number</span><span>];</span></div></div><div><div><div>2</div></div><div><span><span>tom</span><span>[</span></span><span>0</span><span>] </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>tom</span><span>[</span></span><span>1</span><span>] </span><span>=</span><span> </span><span>25</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>tom</span><span>[</span></span><span>0</span><span>].</span><span>slice</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>tom</span><span>[</span></span><span>1</span><span>].</span><span>toFixed</span><span>(</span><span>2</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也可以只赋值其中一项：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> [</span><span>string</span><span>, </span><span>number</span><span>];</span></div></div><div><div><div>2</div></div><div><span><span>tom</span><span>[</span></span><span>0</span><span>] </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是当直接对元组类型的变量进行初始化或者赋值的时候，需要提供所有元组类型中指定的项。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> [</span><span>string</span><span>, </span><span>number</span><span>];</span></div></div><div><div><div>2</div></div><div><span><span>tom</span><span> </span></span><span>=</span><span> [</span><span>'Tom'</span><span>, </span><span>25</span><span>];</span></div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> [</span><span>string</span><span>, </span><span>number</span><span>];</span></div></div><div><div><div>4</div></div><div><span><span>tom</span><span> </span></span><span>=</span><span> [</span><span>'Tom'</span><span>];</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// Property '1' is missing in type '[string]' but required in type '[string, number]'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 第⼀个元素必须是 string 类型,第⼆个元素必须是 number 类型。</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>arr1</span></span><span>:</span><span> [</span><span>string</span><span>,</span><span>number</span><span>]</span></div></div><div><div><div>3</div></div><div><span>// 第⼀个元素必须是 number 类型,第⼆个元素是可选的,如果存在,必须是 boolean 类型。</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>arr2</span></span><span>:</span><span> [</span><span>number</span><span>,</span><span><span>boolean</span><span>?</span></span><span>]</span></div></div><div><div><div>5</div></div><div><span>// 第⼀个元素必须是 number 类型,后⾯的元素可以是任意数量的 string 类型</span></div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>arr3</span></span><span>:</span><span> [</span><span>number</span><span>,</span><span><span>...</span><span>string</span></span><span>[]]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>越界的元素<a href="#越界的元素"><span>#</span></a></h2><p>当添加越界的元素时，它的类型会被限制为元组中每个类型的联合类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> [</span><span>string</span><span>, </span><span>number</span><span>];</span></div></div><div><div><div>2</div></div><div><span><span>tom</span><span> </span></span><span>=</span><span> [</span><span>'Tom'</span><span>, </span><span>25</span><span>];</span></div></div><div><div><div>3</div></div><div><span><span>tom</span><span>.</span></span><span>push</span><span>(</span><span>'male'</span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>tom</span><span>.</span></span><span>push</span><span>(</span><span>true</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// Argument of type 'true' is not assignable to parameter of type 'string | number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>枚举<a href="#枚举"><span>#</span></a></h1><p>枚举（Enum）类型用于取值被限定在一定范围内的场景，比如一周只能有七天，颜色限定为红绿蓝等。</p><section><h2>简单的例子<a href="#简单的例子-1"><span>#</span></a></h2><p>枚举使用 <code>enum</code> 关键字来定义：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> {</span><span>Sun</span><span>, </span><span>Mon</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>枚举成员会被赋值为从 <code>0</code> 开始递增的数字，同时也会对枚举值到枚举名进行反向映射：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> {</span><span>Sun</span><span>, </span><span>Mon</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span>};</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sun"</span><span>] </span><span>===</span><span> </span><span>0</span><span>); </span><span>// true</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Mon"</span><span>] </span><span>===</span><span> </span><span>1</span><span>); </span><span>// true</span></div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Tue"</span><span>] </span><span>===</span><span> </span><span>2</span><span>); </span><span>// true</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sat"</span><span>] </span><span>===</span><span> </span><span>6</span><span>); </span><span>// true</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>0</span><span>] </span><span>===</span><span> </span><span>"Sun"</span><span>); </span><span>// true</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>1</span><span>] </span><span>===</span><span> </span><span>"Mon"</span><span>); </span><span>// true</span></div></div><div><div><div>10</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>2</span><span>] </span><span>===</span><span> </span><span>"Tue"</span><span>); </span><span>// true</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>6</span><span>] </span><span>===</span><span> </span><span>"Sat"</span><span>); </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>事实上，上面的例子会被编译为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>Days</span><span>;</span></span></div></div><div><div><div>2</div></div><div><span>(</span><span>function</span><span><span> (</span><span>Days</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sun"</span><span>] </span><span>=</span><span> </span><span>0</span><span>] </span><span>=</span><span> </span><span>"Sun"</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Mon"</span><span>] </span><span>=</span><span> </span><span>1</span><span>] </span><span>=</span><span> </span><span>"Mon"</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Tue"</span><span>] </span><span>=</span><span> </span><span>2</span><span>] </span><span>=</span><span> </span><span>"Tue"</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Wed"</span><span>] </span><span>=</span><span> </span><span>3</span><span>] </span><span>=</span><span> </span><span>"Wed"</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Thu"</span><span>] </span><span>=</span><span> </span><span>4</span><span>] </span><span>=</span><span> </span><span>"Thu"</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Fri"</span><span>] </span><span>=</span><span> </span><span>5</span><span>] </span><span>=</span><span> </span><span>"Fri"</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sat"</span><span>] </span><span>=</span><span> </span><span>6</span><span>] </span><span>=</span><span> </span><span>"Sat"</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>})(</span><span>Days</span><span> </span></span><span>||</span><span><span> (</span><span>Days</span><span> </span></span><span>=</span><span> {}));</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>手动赋值<a href="#手动赋值"><span>#</span></a></h2><p>我们也可以给枚举项手动赋值：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> {</span><span>Sun</span><span> </span><span>=</span><span> </span><span>7</span><span>, </span><span>Mon</span><span> </span><span>=</span><span> </span><span>1</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span>};</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sun"</span><span>] </span><span>===</span><span> </span><span>7</span><span>); </span><span>// true</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Mon"</span><span>] </span><span>===</span><span> </span><span>1</span><span>); </span><span>// true</span></div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Tue"</span><span>] </span><span>===</span><span> </span><span>2</span><span>); </span><span>// true</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sat"</span><span>] </span><span>===</span><span> </span><span>6</span><span>); </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，未手动赋值的枚举项会接着上一个枚举项递增。</p><p>如果未手动赋值的枚举项与手动赋值的重复了，TypeScript 是不会察觉到这一点的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> {</span><span>Sun</span><span> </span><span>=</span><span> </span><span>3</span><span>, </span><span>Mon</span><span> </span><span>=</span><span> </span><span>1</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span>};</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sun"</span><span>] </span><span>===</span><span> </span><span>3</span><span>); </span><span>// true</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Wed"</span><span>] </span><span>===</span><span> </span><span>3</span><span>); </span><span>// true</span></div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>3</span><span>] </span><span>===</span><span> </span><span>"Sun"</span><span>); </span><span>// false</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>3</span><span>] </span><span>===</span><span> </span><span>"Wed"</span><span>); </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，递增到 <code>3</code> 的时候与前面的 <code>Sun</code> 的取值重复了，但是 TypeScript 并没有报错，导致 <code>Days[3]</code> 的值先是 <code>"Sun"</code>，而后又被 <code>"Wed"</code> 覆盖了。编译的结果是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>Days</span><span>;</span></span></div></div><div><div><div>2</div></div><div><span>(</span><span>function</span><span><span> (</span><span>Days</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sun"</span><span>] </span><span>=</span><span> </span><span>3</span><span>] </span><span>=</span><span> </span><span>"Sun"</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Mon"</span><span>] </span><span>=</span><span> </span><span>1</span><span>] </span><span>=</span><span> </span><span>"Mon"</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Tue"</span><span>] </span><span>=</span><span> </span><span>2</span><span>] </span><span>=</span><span> </span><span>"Tue"</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Wed"</span><span>] </span><span>=</span><span> </span><span>3</span><span>] </span><span>=</span><span> </span><span>"Wed"</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Thu"</span><span>] </span><span>=</span><span> </span><span>4</span><span>] </span><span>=</span><span> </span><span>"Thu"</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Fri"</span><span>] </span><span>=</span><span> </span><span>5</span><span>] </span><span>=</span><span> </span><span>"Fri"</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sat"</span><span>] </span><span>=</span><span> </span><span>6</span><span>] </span><span>=</span><span> </span><span>"Sat"</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>})(</span><span>Days</span><span> </span></span><span>||</span><span><span> (</span><span>Days</span><span> </span></span><span>=</span><span> {}));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>所以使用的时候需要注意，最好不要出现这种覆盖的情况。</p><p>手动赋值的枚举项可以不是数字，此时需要使用类型断言来让 tsc 无视类型检查 (编译出的 js 仍然是可用的)：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> {</span><span>Sun</span><span> </span><span>=</span><span> </span><span>7</span><span>, </span><span>Mon</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span> </span><span>=</span><span> &lt;</span><span>any</span><span>&gt;</span><span>"S"</span><span>};</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>Days</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>(</span><span>function</span><span><span> (</span><span>Days</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sun"</span><span>] </span><span>=</span><span> </span><span>7</span><span>] </span><span>=</span><span> </span><span>"Sun"</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Mon"</span><span>] </span><span>=</span><span> </span><span>8</span><span>] </span><span>=</span><span> </span><span>"Mon"</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Tue"</span><span>] </span><span>=</span><span> </span><span>9</span><span>] </span><span>=</span><span> </span><span>"Tue"</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Wed"</span><span>] </span><span>=</span><span> </span><span>10</span><span>] </span><span>=</span><span> </span><span>"Wed"</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Thu"</span><span>] </span><span>=</span><span> </span><span>11</span><span>] </span><span>=</span><span> </span><span>"Thu"</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Fri"</span><span>] </span><span>=</span><span> </span><span>12</span><span>] </span><span>=</span><span> </span><span>"Fri"</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sat"</span><span>] </span><span>=</span><span> </span><span>"S"</span><span>] </span><span>=</span><span> </span><span>"Sat"</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>})(</span><span>Days</span><span> </span></span><span>||</span><span><span> (</span><span>Days</span><span> </span></span><span>=</span><span> {}));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果不使用类型断言，则不能使用反向查找</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> { </span><span>Sun</span><span> </span><span>=</span><span> </span><span>7</span><span>, </span><span>Mon</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span> </span><span>=</span><span> </span><span>"S"</span><span> };</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>var</span><span><span> </span><span>Days</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>(</span><span>function</span><span><span> (</span><span>Days</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Sun"</span><span>] </span><span>=</span><span> </span><span>7</span><span>] </span><span>=</span><span> </span><span>"Sun"</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Mon"</span><span>] </span><span>=</span><span> </span><span>8</span><span>] </span><span>=</span><span> </span><span>"Mon"</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Tue"</span><span>] </span><span>=</span><span> </span><span>9</span><span>] </span><span>=</span><span> </span><span>"Tue"</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Wed"</span><span>] </span><span>=</span><span> </span><span>10</span><span>] </span><span>=</span><span> </span><span>"Wed"</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Thu"</span><span>] </span><span>=</span><span> </span><span>11</span><span>] </span><span>=</span><span> </span><span>"Thu"</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>Days</span><span>[</span><span>"Fri"</span><span>] </span><span>=</span><span> </span><span>12</span><span>] </span><span>=</span><span> </span><span>"Fri"</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>Days</span><span>[</span><span>"Sat"</span><span>] </span><span>=</span><span> </span><span>"S"</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>})(</span><span>Days</span><span> </span></span><span>||</span><span><span> (</span><span>Days</span><span> </span></span><span>=</span><span> {}));</span></div></div><div><div><div>13</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>'Sat'</span><span><span>], </span><span>Days</span><span>[</span></span><span>'S'</span><span>]); </span><span>// S, undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当然，手动赋值的枚举项也可以为小数或负数，此时后续未手动赋值的项的递增步长仍为 <code>1</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Days</span><span> {</span><span>Sun</span><span> </span><span>=</span><span> </span><span>7</span><span>, </span><span>Mon</span><span> </span><span>=</span><span> </span><span>1.5</span><span>, </span><span>Tue</span><span>, </span><span>Wed</span><span>, </span><span>Thu</span><span>, </span><span>Fri</span><span>, </span><span>Sat</span><span>};</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sun"</span><span>] </span><span>===</span><span> </span><span>7</span><span>); </span><span>// true</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Mon"</span><span>] </span><span>===</span><span> </span><span>1.5</span><span>); </span><span>// true</span></div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Tue"</span><span>] </span><span>===</span><span> </span><span>2.5</span><span>); </span><span>// true</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Days</span><span>[</span></span><span>"Sat"</span><span>] </span><span>===</span><span> </span><span>6.5</span><span>); </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>常数项和计算所得项<a href="#常数项和计算所得项"><span>#</span></a></h2><p>枚举项有两种类型：常数项（constant member）和计算所得项（computed member）。</p><p>前面我们所举的例子都是常数项，一个典型的计算所得项的例子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Color</span><span> {</span><span>Red</span><span>, </span><span>Green</span><span>, </span><span>Blue</span><span> </span><span>=</span><span> </span><span>"blue"</span><span>.</span><span>length</span><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，<code>"blue".length</code> 就是一个计算所得项。</p><p>上面的例子不会报错，但是<strong>如果紧接在计算所得项后面的是未手动赋值的项，那么它就会因为无法获得初始值而报错</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>Color</span><span> {</span><span>Red</span><span> </span><span>=</span><span> </span><span>"red"</span><span>.</span><span>length</span><span>, </span><span>Green</span><span>, </span><span>Blue</span><span>};</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// index.ts(1,33): error TS1061: Enum member must have initializer.</span></div></div><div><div><div>4</div></div><div><span>// index.ts(1,40): error TS1061: Enum member must have initializer.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当满足以下条件时，枚举成员被当作是常数：</p><ul>
<li>不具有初始化函数并且之前的枚举成员是常数。在这种情况下，当前枚举成员的值为上一个枚举成员的值加 <code>1</code>。但第一个枚举元素是个例外。如果它没有初始化方法，那么它的初始值为 <code>0</code>。</li>
<li>枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集，它可以在编译阶段求值。当一个表达式满足下面条件之一时，它就是一个常数枚举表达式：
<ul>
<li>数字字面量</li>
<li>引用之前定义的常数枚举成员（可以是在不同的枚举类型中定义的）如果这个成员是在同一个枚举类型中定义的，可以使用非限定名来引用</li>
<li>带括号的常数枚举表达式</li>
<li><code>+</code>, <code>-</code>, <code>~</code> 一元运算符应用于常数枚举表达式</li>
<li><code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>, <code>&lt;&lt;</code>, <code>&gt;&gt;</code>, <code>&gt;&gt;&gt;</code>, <code>&amp;</code>, <code>|</code>, <code>^</code> 二元运算符，常数枚举表达式做为其一个操作对象。若常数枚举表达式求值后为 NaN 或 Infinity，则会在编译阶段报错</li>
</ul>
</li>
</ul><p>所有其它情况的枚举成员被当作是需要计算得出的值。</p></section><section><h2>常数枚举<a href="#常数枚举"><span>#</span></a></h2><p>常数枚举是使用 <code>const enum</code> 定义的枚举类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>enum</span><span> </span><span>Directions</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>Up</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>Down</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>Left</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>Right</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>directions</span><span> </span></span><span>=</span><span><span> [</span><span>Directions</span><span>.</span></span><span>Up</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Down</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Left</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Right</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>常数枚举与普通枚举的区别是，它会在编译阶段被删除，并且不能包含计算成员。</p><p>上例的编译结果是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>directions</span><span> </span></span><span>=</span><span> [</span><span>0</span><span> </span><span>/* Up */</span><span>, </span><span>1</span><span> </span><span>/* Down */</span><span>, </span><span>2</span><span> </span><span>/* Left */</span><span>, </span><span>3</span><span> </span><span>/* Right */</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>假如包含了计算成员，则会在编译阶段报错：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>enum</span><span> </span><span>Color</span><span> {</span><span>Red</span><span>, </span><span>Green</span><span>, </span><span>Blue</span><span> </span><span>=</span><span> </span><span>"blue"</span><span>.</span><span>length</span><span>};</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// index.ts(1,38): error TS2474: In 'const' enum declarations member initializer must be constant expression.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>外部枚举<a href="#外部枚举"><span>#</span></a></h2><p>外部枚举（Ambient Enums）是使用 <code>declare enum</code> 定义的枚举类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>declare</span><span> </span><span>enum</span><span> </span><span>Directions</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>Up</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>Down</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>Left</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>Right</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>directions</span><span> </span></span><span>=</span><span><span> [</span><span>Directions</span><span>.</span></span><span>Up</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Down</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Left</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Right</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>之前提到过，<code>declare</code> 定义的类型只会用于编译时的检查，编译结果中会被删除。</p><p>上例的编译结果是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>directions</span><span> </span></span><span>=</span><span><span> [</span><span>Directions</span><span>.</span></span><span>Up</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Down</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Left</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Right</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>外部枚举与声明语句一样，常出现在声明文件中。</p><p>同时使用 <code>declare</code> 和 <code>const</code> 也是可以的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>declare</span><span> </span><span>const</span><span> </span><span>enum</span><span> </span><span>Directions</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>Up</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>Down</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>Left</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>Right</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>directions</span><span> </span></span><span>=</span><span><span> [</span><span>Directions</span><span>.</span></span><span>Up</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Down</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Left</span><span><span>, </span><span>Directions</span><span>.</span></span><span>Right</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>编译结果：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>directions</span><span> </span></span><span>=</span><span> [</span><span>0</span><span> </span><span>/* Up */</span><span>, </span><span>1</span><span> </span><span>/* Down */</span><span>, </span><span>2</span><span> </span><span>/* Left */</span><span>, </span><span>3</span><span> </span><span>/* Right */</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>类<a href="#类"><span>#</span></a></h1><p>传统方法中，JavaScript 通过构造函数实现类的概念，通过原型链实现继承。而在 ES6 中，我们终于迎来了 <code>class</code>。</p><p>TypeScript 除了实现了所有 ES6 中的类的功能以外，还添加了一些新的用法。</p><section><h2>类的概念<a href="#类的概念"><span>#</span></a></h2><p>虽然 JavaScript 中有类的概念，但是可能大多数 JavaScript 程序员并不是非常熟悉类，这里对类相关的概念做一个简单的介绍。</p><ul>
<li>类（Class）：定义了一件事物的抽象特点，包含它的属性和方法</li>
<li>对象（Object）：类的实例，通过 <code>new</code> 生成</li>
<li>面向对象（OOP）的三大特性：封装、继承、多态</li>
<li>封装（Encapsulation）：将对数据的操作细节隐藏起来，只暴露对外的接口。外界调用端不需要（也不可能）知道细节，就能通过对外提供的接口来访问该对象，同时也保证了外界无法任意更改对象内部的数据</li>
<li>继承（Inheritance）：子类继承父类，子类除了拥有父类的所有特性外，还有一些更具体的特性</li>
<li>多态（Polymorphism）：由继承而产生了相关的不同的类，对同一个方法可以有不同的响应。比如 <code>Cat</code> 和 <code>Dog</code> 都继承自 <code>Animal</code>，但是分别实现了自己的 <code>eat</code> 方法。此时针对某一个实例，我们无需了解它是 <code>Cat</code> 还是 <code>Dog</code>，就可以直接调用 <code>eat</code> 方法，程序会自动判断出来应该如何执行 <code>eat</code></li>
<li>存取器（getter &amp; setter）：用以改变属性的读取和赋值行为</li>
<li>修饰符（Modifiers）：修饰符是一些关键字，用于限定成员或类型的性质。比如 <code>public</code> 表示公有属性或方法</li>
<li>抽象类（Abstract Class）：抽象类是供其他类继承的基类，抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现</li>
<li>接口（Interfaces）：不同类之间公有的属性或方法，可以抽象成一个接口。接口可以被类实现（implements）。一个类只能继承自另一个类，但是可以实现多个接口</li>
</ul></section><section><h2>ES6 中类的用法<a href="#es6-中类的用法"><span>#</span></a></h2><p>先回顾一下 ES6 中类的用法，更详细的介绍可以参考 <a href="http://es6.ruanyifeng.com/#docs/class" target="_blank">ECMAScript 6 入门 - Class</a>。</p><section><h3>属性和方法<a href="#属性和方法"><span>#</span></a></h3><p>使用 <code>class</code> 定义类，使用 <code>constructor</code> 定义构造函数。</p><p>通过 <code>new</code> 生成新实例的时候，会自动调用构造函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>        </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>sayHi</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>return</span><span> </span><span>`My name is </span><span>${</span><span>this</span><span>.</span><span>name</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>sayHi</span><span>()); </span><span>// My name is Jack</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>类的继承<a href="#类的继承"><span>#</span></a></h3><p>使用 <code>extends</code> 关键字实现继承，子类中使用 <code>super</code> 关键字来调用父类的构造函数和方法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>super</span><span><span>(</span><span>name</span><span>); </span></span><span>// 调用父类的 constructor(name)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>name</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>sayHi</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> </span><span>'Meow, '</span><span> </span><span>+</span><span> </span><span>super</span><span>.</span><span>sayHi</span><span>(); </span><span>// 调用父类的 sayHi()</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>let</span><span><span> </span><span>c</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Cat</span><span>(</span><span>'Tom'</span><span>); </span><span>// Tom</span></div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>c</span><span>.</span></span><span>sayHi</span><span>()); </span><span>// Meow, My name is Tom</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>存取器<a href="#存取器"><span>#</span></a></h3><p>使用 getter 和 setter 可以改变属性的赋值和读取行为：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>  </span><span>get</span><span> </span><span>name</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span> </span><span>'Jack'</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>  </span><span>set</span><span> </span><span>name</span><span><span>(</span><span>value</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'setter: '</span><span> </span><span>+</span><span><span> </span><span>value</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Kitty'</span><span>); </span><span>// setter: Kitty</span></div></div><div><div><div>14</div></div><div><span><span>a</span><span>.</span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>; </span><span>// setter: Tom</span></div></div><div><div><div>15</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>); </span><span>// Jack</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>静态方法<a href="#静态方法"><span>#</span></a></h3><p>使用 <code>static</code> 修饰符修饰的方法称为静态方法，它们不需要实例化，而是直接通过类来调用：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>static</span><span> </span><span>isAnimal</span><span><span>(</span><span>a</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span><span> </span><span>a</span><span> </span></span><span>instanceof</span><span> </span><span>Animal</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>Animal</span><span>.</span></span><span>isAnimal</span><span><span>(</span><span>a</span><span>); </span></span><span>// true</span></div></div><div><div><div>9</div></div><div><span><span>a</span><span>.</span></span><span>isAnimal</span><span><span>(</span><span>a</span><span>); </span></span><span>// TypeError: a.isAnimal is not a function</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>ES7 中类的用法<a href="#es7-中类的用法"><span>#</span></a></h2><p>ES7 中有一些关于类的提案，TypeScript 也实现了它们，这里做一个简单的介绍。</p><section><h3>实例属性<a href="#实例属性"><span>#</span></a></h3><p>ES6 中实例的属性只能通过构造函数中的 <code>this.xxx</code> 来定义，ES7 提案中可以直接在类里面定义：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Jack'</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>();</span></div></div><div><div><div>10</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>); </span><span>// Jack</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>静态属性<a href="#静态属性"><span>#</span></a></h3><p>ES7 提案中，可以使用 <code>static</code> 定义一个静态属性：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>static</span><span><span> </span><span>num</span><span> </span></span><span>=</span><span> </span><span>42</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Animal</span><span>.</span></span><span>num</span><span>); </span><span>// 42</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>TypeScript 中类的用法<a href="#typescript-中类的用法"><span>#</span></a></h2><section><h3>public private 和 protected<a href="#public-private-和-protected"><span>#</span></a></h3><p>TypeScript 可以使用三种访问修饰符（Access Modifiers），分别是 <code>public</code>、<code>private</code> 和 <code>protected</code>。</p><ul>
<li><code>public</code> 修饰的属性或方法是公有的，可以在任何地方被访问到，默认所有的属性和方法都是 <code>public</code> 的</li>
<li><code>private</code> 修饰的属性或方法是私有的，不能在声明它的类的外部访问</li>
<li><code>protected</code> 修饰的属性或方法是受保护的，它和 <code>private</code> 类似，区别是它在子类中也是允许被访问的</li>
</ul><p>下面举一些例子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>public</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>); </span><span>// Jack</span></div></div><div><div><div>10</div></div><div><span><span>a</span><span>.</span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>); </span><span>// Tom</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，<code>name</code> 被设置为了 <code>public</code>，所以直接访问实例的 <code>name</code> 属性是允许的。</p><p>很多时候，我们希望有的属性是无法直接存取的，这时候就可以用 <code>private</code> 了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>private</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>a</span><span>.</span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// index.ts(9,13): error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></div></div><div><div><div>13</div></div><div><span>// index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>需要注意的是，TypeScript 编译之后的代码中，并没有限制 <code>private</code> 属性在外部的可访问性。</p><p>上面的例子编译后的代码是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>Animal</span><span> </span></span><span>=</span><span> (</span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>function</span><span> </span><span>Animal</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>  </span><span>return</span><span><span> </span><span>Animal</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>})();</span></div></div><div><div><div>7</div></div><div><span>var</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>a</span><span>.</span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用 <code>private</code> 修饰的属性或方法，在子类中也是不允许访问的：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>private</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>9</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>super</span><span><span>(</span><span>name</span><span>);</span></span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>name</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// index.ts(11,17): error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>而如果是用 <code>protected</code> 修饰，则允许在子类中访问：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>protected</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>9</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>super</span><span><span>(</span><span>name</span><span>);</span></span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>name</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当构造函数修饰为 <code>private</code> 时，该类不允许被继承或者实例化：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>public</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>private</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>super</span><span><span>(</span><span>name</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class constructor is marked as private.</span></div></div><div><div><div>16</div></div><div><span>// index.ts(13,9): TS2673: Constructor of class 'Animal' is private and only accessible within the class declaration.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>当构造函数修饰为 <code>protected</code> 时，该类只允许被继承：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>public</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>protected</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>super</span><span><span>(</span><span>name</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// index.ts(13,9): TS2674: Constructor of class 'Animal' is protected and only accessible within the class declaration.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>参数属性<a href="#参数属性"><span>#</span></a></h3><p>修饰符和<code>readonly</code>还可以使用在构造函数参数中，等同于类中定义该属性同时给该属性赋值，使代码更简洁。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// public name: string;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span>(</span><span>public</span><span><span> </span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>// this.name = name;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>readonly<a href="#readonly"><span>#</span></a></h3><p>只读属性关键字，只允许出现在属性声明或索引签名或构造函数中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>readonly</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>name</span><span>); </span><span>// Jack</span></div></div><div><div><div>10</div></div><div><span><span>a</span><span>.</span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意如果 <code>readonly</code> 和其他访问修饰符同时存在的话，需要写在其后面。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// public readonly name;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span>(</span><span>public</span><span> </span><span>readonly</span><span><span> </span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>// this.name = name;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>抽象类<a href="#抽象类"><span>#</span></a></h3><p><code>abstract</code> 用于定义抽象类和其中的抽象方法。</p><p>什么是抽象类？</p><p>首先，抽象类是不允许被实例化的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>abstract</span><span> </span><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>public</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>public</span><span> </span><span>abstract</span><span> </span><span>sayHi</span><span>();</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，我们定义了一个抽象类 <code>Animal</code>，并且定义了一个抽象方法 <code>sayHi</code>。在实例化抽象类的时候报错了。</p><p>其次，抽象类中的抽象方法必须被子类实现：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>abstract</span><span> </span><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>public</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>public</span><span> </span><span>abstract</span><span> </span><span>sayHi</span><span>();</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>public</span><span> </span><span>eat</span><span>() {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`</span><span>${</span><span>this</span><span>.</span><span>name</span><span>}</span><span> is eating.`</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>let</span><span><span> </span><span>cat</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Cat</span><span>(</span><span>'Tom'</span><span>);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// index.ts(9,7): error TS2515: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面的例子中，我们定义了一个类 <code>Cat</code> 继承了抽象类 <code>Animal</code>，但是没有实现抽象方法 <code>sayHi</code>，所以编译报错了。</p><p>下面是一个正确使用抽象类的例子：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>abstract</span><span> </span><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>public</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>public</span><span> </span><span>constructor</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>public</span><span> </span><span>abstract</span><span> </span><span>sayHi</span><span>();</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>class</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>public</span><span> </span><span>sayHi</span><span>() {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Meow, My name is </span><span>${</span><span>this</span><span>.</span><span>name</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>let</span><span><span> </span><span>cat</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Cat</span><span>(</span><span>'Tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面的例子中，我们实现了抽象方法 <code>sayHi</code>，编译通过了。</p><p>需要注意的是，即使是抽象方法，TypeScript 的编译结果中，仍然会存在这个类，上面的代码的编译结果是：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>__extends</span><span> </span></span><span>=</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>(</span><span>this</span><span> </span><span>&amp;&amp;</span><span> </span><span>this</span><span>.</span><span>__extends</span><span>) </span><span>||</span></div></div><div><div><div>3</div></div><div><span>  </span><span>function</span><span><span> (</span><span>d</span><span>, </span><span>b</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>for</span><span> (</span><span>var</span><span><span> </span><span>p</span><span> </span></span><span>in</span><span><span> </span><span>b</span><span>) </span></span><span>if</span><span><span> (</span><span>b</span><span>.</span></span><span>hasOwnProperty</span><span><span>(</span><span>p</span><span>)) </span><span>d</span><span>[</span><span>p</span><span>] </span></span><span>=</span><span><span> </span><span>b</span><span>[</span><span>p</span><span>];</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>function</span><span> </span><span>__</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>this</span><span>.</span><span>constructor</span><span> </span><span>=</span><span><span> </span><span>d</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>    </span><span>d</span><span>.</span><span>prototype</span><span> </span><span>=</span><span><span> </span><span>b</span><span> </span></span><span>===</span><span> </span><span>null</span><span> </span><span>?</span><span><span> </span><span>Object</span><span>.</span></span><span>create</span><span><span>(</span><span>b</span><span>) </span></span><span>:</span><span> ((</span><span>__</span><span>.</span><span>prototype</span><span> </span><span>=</span><span> </span><span>b</span><span>.</span><span>prototype</span><span>), </span><span>new</span><span> </span><span>__</span><span>());</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>10</div></div><div><span>var</span><span><span> </span><span>Animal</span><span> </span></span><span>=</span><span> (</span><span>function</span><span> () {</span></div></div><div><div><div>11</div></div><div><span>  </span><span>function</span><span> </span><span>Animal</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>  </span><span>return</span><span><span> </span><span>Animal</span><span>;</span></span></div></div><div><div><div>15</div></div><div><span>})();</span></div></div><div><div><div>16</div></div><div><span>var</span><span><span> </span><span>Cat</span><span> </span></span><span>=</span><span> (</span><span>function</span><span><span> (</span><span>_super</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>  </span><span>__extends</span><span><span>(</span><span>Cat</span><span>, </span><span>_super</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span>  </span><span>function</span><span> </span><span>Cat</span><span>() {</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>_super</span><span>.</span><span>apply</span><span>(</span><span>this</span><span>, </span><span>arguments</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>  </span><span>Cat</span><span>.</span><span>prototype</span><span>.</span><span>sayHi</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Meow, My name is '</span><span> </span><span>+</span><span> </span><span>this</span><span>.</span><span>name</span><span>);</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>24</div></div><div><span>  </span><span>return</span><span><span> </span><span>Cat</span><span>;</span></span></div></div><div><div><div>25</div></div><div><span><span>})(</span><span>Animal</span><span>);</span></span></div></div><div><div><div>26</div></div><div><span>var</span><span><span> </span><span>cat</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Cat</span><span>(</span><span>'Tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>类的类型<a href="#类的类型"><span>#</span></a></h2><p>给类加上 TypeScript 的类型很简单，与接口类似：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>name</span></span><span>:</span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>sayHi</span><span>()</span><span>:</span><span> </span><span>string</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> </span><span>`My name is </span><span>${</span><span>this</span><span>.</span><span>name</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>let</span><span><span> </span><span>a</span></span><span>:</span><span> </span><span>Animal</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Animal</span><span>(</span><span>'Jack'</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>.</span></span><span>sayHi</span><span>()); </span><span>// My name is Jack</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>类实现接口<a href="#类实现接口"><span>#</span></a></h2><p>实现（implements）是面向对象中的一个重要概念。一般来讲，一个类只能继承自另一个类，有时候不同类之间可以有一些共有的特性，这时候就可以把特性提取成接口（interfaces），用 <code>implements</code> 关键字来实现。这个特性大大提高了面向对象的灵活性。</p><p>举例来说，门是一个类，防盗门是门的子类。如果防盗门有一个报警器的功能，我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类，车，也有报警器的功能，就可以考虑把报警器提取出来，作为一个接口，防盗门和车都去实现它：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>alert</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>class</span><span> </span><span>Door</span><span> {</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>class</span><span> </span><span>SecurityDoor</span><span> </span><span>extends</span><span> </span><span>Door</span><span> </span><span>implements</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>alert</span><span>() {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'SecurityDoor alert'</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>class</span><span> </span><span>Car</span><span> </span><span>implements</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>alert</span><span>() {</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Car alert'</span><span>);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>一个类可以实现多个接口：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>alert</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Light</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>lightOn</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>lightOff</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>class</span><span> </span><span>Car</span><span> </span><span>implements</span><span> </span><span>Alarm</span><span>, </span><span>Light</span><span> {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>alert</span><span>() {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Car alert'</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>    </span><span>lightOn</span><span>() {</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Car light on'</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>    </span><span>lightOff</span><span>() {</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Car light off'</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上例中，<code>Car</code> 实现了 <code>Alarm</code> 和 <code>Light</code> 接口，既能报警，也能开关车灯。</p></section><section><h2>接口继承接口<a href="#接口继承接口"><span>#</span></a></h2><p>接口与接口之间可以是继承关系：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>alert</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>LightableAlarm</span><span> </span><span>extends</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>lightOn</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>lightOff</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这很好理解，<code>LightableAlarm</code> 继承了 <code>Alarm</code>，除了拥有 <code>alert</code> 方法之外，还拥有两个新方法 <code>lightOn</code> 和 <code>lightOff</code>。</p></section><section><h2>接口继承类<a href="#接口继承类"><span>#</span></a></h2><p>常见的面向对象语言中，接口是不能继承类的，但是在 TypeScript 中却是可以的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>        </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>interface</span><span> </span><span>Point3d</span><span> </span><span>extends</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>z</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>let</span><span><span> </span><span>point3d</span></span><span>:</span><span> </span><span>Point3d</span><span> </span><span>=</span><span> {</span><span>x</span><span>:</span><span> </span><span>1</span><span>, </span><span>y</span><span>:</span><span> </span><span>2</span><span>, </span><span>z</span><span>:</span><span> </span><span>3</span><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>为什么 TypeScript 会支持接口继承类呢？</p><p>实际上，当我们在声明 <code>class Point</code> 时，除了会创建一个名为 <code>Point</code> 的类之外，同时也创建了一个名为 <code>Point</code> 的类型（实例的类型）。类本身可以作为类型使用，接口继承类本质上就是<strong>接口继承了该类实例的“形状”（Shape）</strong>，但不继承它的实现。</p><p>所以我们既可以将 <code>Point</code> 当做一个类来用（使用 <code>new Point</code> 创建它的实例）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>        </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>p</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Point</span><span>(</span><span>1</span><span>, </span><span>2</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也可以将 <code>Point</code> 当做一个类型来用（使用 <code>: Point</code> 表示参数的类型）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>        </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>printPoint</span><span><span>(</span><span>p</span></span><span>:</span><span> </span><span>Point</span><span>) {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p</span><span>.</span></span><span>x</span><span><span>, </span><span>p</span><span>.</span></span><span>y</span><span>);</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>printPoint</span><span>(</span><span>new</span><span> </span><span>Point</span><span>(</span><span>1</span><span>, </span><span>2</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这个例子实际上可以等价于：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>        </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>interface</span><span> </span><span>PointInstanceType</span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>function</span><span> </span><span>printPoint</span><span><span>(</span><span>p</span></span><span>:</span><span> </span><span>PointInstanceType</span><span>) {</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p</span><span>.</span></span><span>x</span><span><span>, </span><span>p</span><span>.</span></span><span>y</span><span>);</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>printPoint</span><span>(</span><span>new</span><span> </span><span>Point</span><span>(</span><span>1</span><span>, </span><span>2</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上例中我们新声明的 <code>PointInstanceType</code> 类型，与声明 <code>class Point</code> 时创建的 <code>Point</code> 类型是等价的。</p><p>所以回到 <code>Point3d</code> 的例子中，我们就能很容易的理解为什么 TypeScript 会支持接口继承类了：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>        </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>interface</span><span> </span><span>PointInstanceType</span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 等价于 interface Point3d extends PointInstanceType</span></div></div><div><div><div>16</div></div><div><span>interface</span><span> </span><span>Point3d</span><span> </span><span>extends</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>z</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>let</span><span><span> </span><span>point3d</span></span><span>:</span><span> </span><span>Point3d</span><span> </span><span>=</span><span> {</span><span>x</span><span>:</span><span> </span><span>1</span><span>, </span><span>y</span><span>:</span><span> </span><span>2</span><span>, </span><span>z</span><span>:</span><span> </span><span>3</span><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>当我们声明 <code>interface Point3d extends Point</code> 时，<code>Point3d</code> 继承的实际上是类 <code>Point</code> 的实例的类型。</p><p>换句话说，可以理解为定义了一个接口 <code>Point3d</code> 继承另一个接口 <code>PointInstanceType</code>。</p><p>所以「接口继承类」和「接口继承接口」没有什么本质的区别。</p><p>值得注意的是，<code>PointInstanceType</code> 相比于 <code>Point</code>，缺少了 <code>constructor</code> 方法，这是因为声明 <code>Point</code> 类时创建的 <code>Point</code> 类型是不包含构造函数的。另外，除了构造函数是不包含的，静态属性或静态方法也是不包含的（实例的类型当然不应该包括构造函数、静态属性或静态方法）。</p><p>换句话说，声明 <code>Point</code> 类时创建的 <code>Point</code> 类型只包含其中的<strong>实例属性</strong>和<strong>实例方法</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>/** 静态属性，坐标系原点 */</span></div></div><div><div><div>3</div></div><div><span>    </span><span>static</span><span><span> </span><span>origin</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Point</span><span>(</span><span>0</span><span>, </span><span>0</span><span>);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>/** 静态方法，计算与原点距离 */</span></div></div><div><div><div>5</div></div><div><span>    </span><span>static</span><span> </span><span>distanceToOrigin</span><span><span>(</span><span>p</span></span><span>:</span><span> </span><span>Point</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>return</span><span><span> </span><span>Math</span><span>.</span></span><span>sqrt</span><span><span>(</span><span>p</span><span>.</span></span><span>x</span><span> </span><span>*</span><span><span> </span><span>p</span><span>.</span></span><span>x</span><span> </span><span>+</span><span><span> </span><span>p</span><span>.</span></span><span>y</span><span> </span><span>*</span><span><span> </span><span>p</span><span>.</span></span><span>y</span><span>);</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>    </span><span>/** 实例属性，x 轴的值 */</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>/** 实例属性，y 轴的值 */</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>/** 构造函数 */</span></div></div><div><div><div>13</div></div><div><span>    </span><span>constructor</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>15</div></div><div><span>        </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>    </span><span>/** 实例方法，打印此点 */</span></div></div><div><div><div>18</div></div><div><span>    </span><span>printPoint</span><span>() {</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>x</span><span>, </span><span>this</span><span>.</span><span>y</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>interface</span><span> </span><span>PointInstanceType</span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>x</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>y</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>26</div></div><div><span>    </span><span>printPoint</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>let</span><span><span> </span><span>p1</span></span><span>:</span><span> </span><span>Point</span><span>;</span></div></div><div><div><div>30</div></div><div><span>let</span><span><span> </span><span>p2</span></span><span>:</span><span> </span><span>PointInstanceType</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上例中最后的类型 <code>Point</code> 和类型 <code>PointInstanceType</code> 是等价的。</p><p>同样的，在接口继承类的时候，也只会继承它的实例属性和实例方法。</p><section><h3>为什么要支持这种操作？<a href="#为什么要支持这种操作"><span>#</span></a></h3><p>这在实际开发中（尤其是库开发或大型架构）有两个非常巧妙的用途：</p><ul>
<li>抽离既有实现的“形状”</li>
</ul><p>如果你已经写了一个复杂的类，现在需要定义一个结构相似的接口，直接 <code>extends</code> 那个类可以让<strong>少写很多重复代码</strong>。这在重构旧的 JS 项目到 TS 时非常有用。</p><ul>
<li>限制接口的“实现者”（私有成员魔法）</li>
</ul><p>这是最核心的进阶技巧。如果类中包含 <strong><code>private</code></strong> 或 <strong><code>protected</code></strong> 成员，那么继承该类的接口也会包含这些私有成员。</p><p>这意味着：只有这个类及其子类，才能实现这个接口。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>Control</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>private</span><span><span> </span><span>state</span></span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// SelectableControl 继承了 Control，包括私有属性 state</span></div></div><div><div><div>6</div></div><div><span>interface</span><span> </span><span>SelectableControl</span><span> </span><span>extends</span><span> </span><span>Control</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>select</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 成功：Button 是 Control 的子类，拥有 state 属性</span></div></div><div><div><div>11</div></div><div><span>class</span><span> </span><span>Button</span><span> </span><span>extends</span><span> </span><span>Control</span><span> </span><span>implements</span><span> </span><span>SelectableControl</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>select</span><span>() { }</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 报错：Image 不是 Control 的子类，它缺少那个特定的私有属性 state</span></div></div><div><div><div>16</div></div><div><span>class</span><span> </span><span>Image</span><span> </span><span>implements</span><span> </span><span>SelectableControl</span><span> {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>select</span><span>() { }</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这种设计可以用来<strong>强制执行类层级结构</strong>，确保某些接口只能被特定的组件系列使用。</p></section></section></section>
<section><h1>泛型<a href="#泛型"><span>#</span></a></h1><p>泛型（Generics）是指在定义函数、接口或类的时候，不预先指定具体的类型，而在使用的时候再指定类型的一种特性。</p><p>可以简单的理解为**“类型的参数”**，动态设置类型。</p><section><h2>简单的例子<a href="#简单的例子-2"><span>#</span></a></h2><p>首先，我们来实现一个函数 <code>createArray</code>，它可以创建一个指定长度的数组，同时将每一项都填充一个默认值：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>createArray</span><span><span>(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>any</span><span>)</span><span>:</span><span> </span><span>Array</span><span><span>&lt;</span><span>any</span><span>&gt; {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span><span> </span></span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>result</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>createArray</span><span>(</span><span>3</span><span>, </span><span>'x'</span><span>); </span><span>// ['x', 'x', 'x']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，使用了数组泛型来定义返回值的类型。</p><p>这段代码编译不会报错，但是一个显而易见的缺陷是，它并没有准确的定义返回值的类型：</p><p><code>Array&lt;any&gt;</code> 允许数组的每一项都为任意类型。但是我们预期的是，数组中每一项都应该是输入的 <code>value</code> 的类型。</p><p>这时候，泛型就派上用场了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>createArray</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span></span><span>:</span><span> </span><span>T</span><span>[] </span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>result</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>createArray</span><span><span>&lt;</span><span>string</span><span>&gt;(</span></span><span>3</span><span>, </span><span>'x'</span><span>); </span><span>// ['x', 'x', 'x']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，我们在函数名后添加了 <code>&lt;T&gt;</code>，其中 <code>T</code> 用来指代任意输入的类型，在后面的输入 <code>value: T</code> 和输出 <code>Array&lt;T&gt;</code> 中即可使用了。这里的 <code>&lt;T&gt;</code> 就像一个占位符。当你调用函数时，<code>T</code> 会被具体类型（如 <code>string</code>）替换。</p><p>接着在调用的时候，可以指定它具体的类型为 <code>string</code>。当然，也可以不手动指定，而让类型推论自动推算出来：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>createArray</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span></span><span>:</span><span> </span><span>T</span><span>[] </span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>result</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>createArray</span><span>(</span><span>3</span><span>, </span><span>'x'</span><span>); </span><span>// ['x', 'x', 'x']</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>多个类型参数<a href="#多个类型参数"><span>#</span></a></h2><p>定义泛型的时候，可以一次定义多个类型参数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>swap</span><span>&lt;</span><span>T</span><span>, </span><span>U</span><span><span>&gt;(</span><span>tuple</span></span><span>:</span><span> [</span><span>T</span><span>, </span><span>U</span><span>])</span><span>:</span><span> [</span><span>U</span><span>, </span><span>T</span><span>] {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> [</span><span>tuple</span><span>[</span></span><span>1</span><span><span>], </span><span>tuple</span><span>[</span></span><span>0</span><span>]];</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>swap</span><span>([</span><span>7</span><span>, </span><span>'seven'</span><span>]); </span><span>// ['seven', 7]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，我们定义了一个 <code>swap</code> 函数，用来交换输入的元组。</p></section><section><h2>泛型约束<a href="#泛型约束"><span>#</span></a></h2><p>在函数内部使用泛型变量的时候，由于事先不知道它是哪种类型，所以不能随意的操作它的属性或方法：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>loggingIdentity</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>arg</span><span>.</span></span><span>length</span><span>);</span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，泛型 <code>T</code> 不一定包含属性 <code>length</code>，所以编译的时候报错了。</p><p>这时，我们可以对泛型进行约束，只允许这个函数传入那些包含 <code>length</code> 属性的变量。这就是泛型约束：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Lengthwise</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>length</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>loggingIdentity</span><span>&lt;</span><span>T</span><span> </span><span>extends</span><span> </span><span>Lengthwise</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>arg</span><span>.</span></span><span>length</span><span>);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，我们使用了 <code>extends</code> 约束了泛型 <code>T</code> 必须符合接口 <code>Lengthwise</code> 的形状，也就是必须包含 <code>length</code> 属性。</p><p>此时如果调用 <code>loggingIdentity</code> 的时候，传入的 <code>arg</code> 不包含 <code>length</code>，那么在编译阶段就会报错了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Lengthwise</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>length</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>loggingIdentity</span><span>&lt;</span><span>T</span><span> </span><span>extends</span><span> </span><span>Lengthwise</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>arg</span><span>.</span></span><span>length</span><span>);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>loggingIdentity</span><span>(</span><span>7</span><span>);</span></div></div><div><div><div>11</div></div><div><span>loggingIdentity</span><span>([</span><span>7</span><span>]); </span><span>// 1</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>多个类型参数之间也可以互相约束：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>copyFields</span><span>&lt;</span><span>T</span><span> </span><span>extends</span><span> </span><span>U</span><span>, </span><span>U</span><span><span>&gt;(</span><span>target</span></span><span>:</span><span> </span><span>T</span><span><span>, </span><span>source</span></span><span>:</span><span> </span><span>U</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>id</span><span> </span></span><span>in</span><span><span> </span><span>source</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>target</span><span>[</span><span>id</span><span>] </span><span>=</span><span> (&lt;</span><span>T</span><span><span>&gt;</span><span>source</span><span>)[</span><span>id</span><span>];</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span><span> </span><span>target</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>x</span><span> </span></span><span>=</span><span> { </span><span>a</span><span>:</span><span> </span><span>1</span><span>, </span><span>b</span><span>:</span><span> </span><span>2</span><span>, </span><span>c</span><span>:</span><span> </span><span>3</span><span>, </span><span>d</span><span>:</span><span> </span><span>4</span><span> };</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>copyFields</span><span><span>(</span><span>x</span><span>, { </span></span><span>b</span><span>:</span><span> </span><span>10</span><span>, </span><span>d</span><span>:</span><span> </span><span>20</span><span> });</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，我们使用了两个类型参数，其中要求 <code>T</code> 继承 <code>U</code>，这样就保证了 <code>U</code> 上不会出现 <code>T</code> 中不存在的字段。</p></section><section><h2>泛型接口<a href="#泛型接口"><span>#</span></a></h2><p>可以使用接口的方式来定义一个函数需要符合的形状：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>SearchFunc</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>(</span><span>source</span><span>:</span><span> </span><span>string</span><span><span>, </span><span>subString</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>boolean</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>mySearch</span></span><span>:</span><span> </span><span>SearchFunc</span><span>;</span></div></div><div><div><div>6</div></div><div><span>mySearch</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>source</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>subString</span></span><span>:</span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>source</span><span>.</span></span><span>search</span><span><span>(</span><span>subString</span><span>) </span></span><span>!==</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当然也可以使用含有泛型的接口来定义函数的形状：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>CreateArrayFunc</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt;;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>createArray</span></span><span>:</span><span> </span><span>CreateArrayFunc</span><span>;</span></div></div><div><div><div>6</div></div><div><span>createArray</span><span> </span><span>=</span><span> </span><span>function</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span></span><span>:</span><span> </span><span>T</span><span>[] </span><span>=</span><span> [];</span></div></div><div><div><div>8</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>result</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>createArray</span><span>(</span><span>3</span><span>, </span><span>'x'</span><span>); </span><span>// ['x', 'x', 'x']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>&lt;T&gt;</code> 写在括号前面，意味着这是一个<strong>泛型函数接口</strong>。</p><p>它的含义是：“任何实现这个接口的函数，必须接受一个类型 <code>T</code>，一个数字类型的 <code>length</code>，以及一个类型为 <code>T</code> 的 <code>value</code>，最后返回一个元素类型全是 <code>T</code> 的数组。”，在<strong>调用函数时</strong>才确定 <code>T</code> 是什么类型。</p><p>进一步，我们可以把泛型参数提前到接口名上：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>CreateArrayFunc</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>(</span><span>length</span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt;;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>createArray</span></span><span>:</span><span> </span><span>CreateArrayFunc</span><span><span>&lt;</span><span>any</span><span>&gt;;</span></span></div></div><div><div><div>6</div></div><div><span>createArray</span><span> </span><span>=</span><span> </span><span>function</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span></span><span>:</span><span> </span><span>T</span><span>[] </span><span>=</span><span> [];</span></div></div><div><div><div>8</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>result</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>createArray</span><span>(</span><span>3</span><span>, </span><span>'x'</span><span>); </span><span>// ['x', 'x', 'x']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，此时在使用泛型接口的时候，需要定义泛型的类型。在<strong>定义接口时</strong>就得确定 <code>T</code> 是什么。此处 <code>T</code> 为<code>any</code>，与上面代码意思相同，但是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>createArray</span></span><span>:</span><span> </span><span>CreateArrayFunc</span><span><span>&lt;</span><span>string</span><span>&gt;;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>只能接收创建<code>string</code>类型参数，而<code>number</code>类型会报错。</p></section><section><h2>泛型类<a href="#泛型类"><span>#</span></a></h2><p>与泛型接口类似，泛型也可以用于类的类型定义中：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>GenericNumber</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>zeroValue</span><span>:</span><span> </span><span>T</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>add</span><span>:</span><span><span> (</span><span>x</span></span><span>:</span><span> </span><span>T</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>T</span><span>) </span><span>=&gt;</span><span> </span><span>T</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>myGenericNumber</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>GenericNumber</span><span><span>&lt;</span><span>number</span><span>&gt;();</span></span></div></div><div><div><div>7</div></div><div><span><span>myGenericNumber</span><span>.</span></span><span>zeroValue</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>myGenericNumber</span><span>.</span></span><span>add</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>x</span><span>, </span><span>y</span><span>) { </span></span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>; };</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>泛型参数的默认类型<a href="#泛型参数的默认类型"><span>#</span></a></h2><p>在 TypeScript 2.3 以后，我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数，从实际值参数中也无法推测出时，这个默认类型就会起作用。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>createArray</span><span>&lt;</span><span>T</span><span> </span><span>=</span><span><span> </span><span>string</span><span>&gt;(</span><span>length</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span></span><span>:</span><span> </span><span>T</span><span>[] </span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>result</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>声明合并<a href="#声明合并"><span>#</span></a></h1><p>如果定义了两个相同名字的函数、接口或类，那么它们会合并成一个类型：</p><section><h2>函数的合并<a href="#函数的合并"><span>#</span></a></h2><p>我们可以使用重载定义多个函数类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>string</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>'number'</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>return</span><span> </span><span>Number</span><span><span>(</span><span>x</span><span>.</span></span><span>toString</span><span>().</span><span>split</span><span>(</span><span>''</span><span>).</span><span>reverse</span><span>().</span><span>join</span><span>(</span><span>''</span><span>));</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>'string'</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>return</span><span><span> </span><span>x</span><span>.</span></span><span>split</span><span>(</span><span>''</span><span>).</span><span>reverse</span><span>().</span><span>join</span><span>(</span><span>''</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>接口的合并<a href="#接口的合并"><span>#</span></a></h2><p>接口中的属性在合并时会简单的合并到一个接口中：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>weight</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>相当于：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>weight</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，<strong>合并的属性的类型必须是唯一的</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;  </span><span>// 虽然重复了，但是类型都是 `number`，所以不会报错</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>weight</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>string</span><span>;  </span><span>// 类型不一致，会报错</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>weight</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type.  Variable 'price' must be of type 'number', but here has type 'string'.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>接口中方法的合并，与函数的合并一样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>alert</span><span><span>(</span><span>s</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>weight</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>alert</span><span><span>(</span><span>s</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>n</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>相当于：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Alarm</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>price</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>weight</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>alert</span><span><span>(</span><span>s</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>alert</span><span><span>(</span><span>s</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>n</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>类的合并<a href="#类的合并"><span>#</span></a></h2><p>类的合并与接口的合并规则一致。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/typescript-ji-chu/</id>
      <title type="text">TypeScript 基础</title>
      <published>2026-03-25T06:12:32.000Z</published>
      <updated>2026-03-25T06:12:32.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/typescript-ji-chu/"/>
      <summary type="text">JavaScript 的类型分为两种：原始数据类型（Primitive data types）和对象类型（Object types）。 原始数据类型包括：布尔值、数值、字符串、 null 、 undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt 。</summary>
      <content type="html"><![CDATA[<section><h1>原始数据类型<a href="#原始数据类型"><span>#</span></a></h1><p>JavaScript 的类型分为两种：原始数据类型（<a href="https://developer.mozilla.org/en-US/docs/Glossary/Primitive" target="_blank">Primitive data types</a>）和对象类型（Object types）。</p><p>原始数据类型包括：布尔值、数值、字符串、<code>null</code>、<code>undefined</code> 以及 ES6 中的新类型 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol" target="_blank"><code>Symbol</code></a> 和 ES10 中的新类型 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt" target="_blank"><code>BigInt</code></a>。</p><section><h2>布尔值<a href="#布尔值"><span>#</span></a></h2><p>布尔值是最基础的数据类型，在 TypeScript 中，使用 <code>boolean</code> 定义布尔值类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>isDone</span></span><span>:</span><span> </span><span>boolean</span><span> </span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 编译通过</span></div></div><div><div><div>4</div></div><div><span>// 后面约定，未强调编译错误的代码片段，默认为编译通过</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，使用构造函数 <code>Boolean</code> 创造的对象<strong>不是</strong>布尔值：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>createdByNewBoolean</span></span><span>:</span><span> </span><span>boolean</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Boolean</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// Type 'Boolean' is not assignable to type 'boolean'.</span></div></div><div><div><div>4</div></div><div><span>//   'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>事实上 <code>new Boolean()</code> 返回的是一个 <code>Boolean</code> 对象：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>createdByNewBoolean</span></span><span>:</span><span> </span><span>Boolean</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Boolean</span><span>(</span><span>1</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>直接调用 <code>Boolean</code> 也可以返回一个 <code>boolean</code> 类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>createdByBoolean</span></span><span>:</span><span> </span><span>boolean</span><span> </span><span>=</span><span> </span><span>Boolean</span><span>(</span><span>1</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 TypeScript 中，<code>boolean</code> 是 JavaScript 中的基本类型，而 <code>Boolean</code> 是 JavaScript 中的构造函数。其他基本类型（除了 <code>null</code> 和 <code>undefined</code>）一样，不再赘述。</p></section><section><h2>数值<a href="#数值"><span>#</span></a></h2><p>使用 <code>number</code> 定义数值类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>decLiteral</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>6</span><span>;</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>hexLiteral</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>0xf00d</span><span>;</span></div></div><div><div><div>3</div></div><div><span>// ES6 中的二进制表示法</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>binaryLiteral</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>0b1010</span><span>;</span></div></div><div><div><div>5</div></div><div><span>// ES6 中的八进制表示法</span></div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>octalLiteral</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>0o744</span><span>;</span></div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>notANumber</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>NaN</span><span>;</span></div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>infinityNumber</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>Infinity</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>编译结果：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>decLiteral</span><span> </span></span><span>=</span><span> </span><span>6</span><span>;</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>hexLiteral</span><span> </span></span><span>=</span><span> </span><span>0xf00d</span><span>;</span></div></div><div><div><div>3</div></div><div><span>// ES6 中的二进制表示法</span></div></div><div><div><div>4</div></div><div><span>var</span><span><span> </span><span>binaryLiteral</span><span> </span></span><span>=</span><span> </span><span>10</span><span>;</span></div></div><div><div><div>5</div></div><div><span>// ES6 中的八进制表示法</span></div></div><div><div><div>6</div></div><div><span>var</span><span><span> </span><span>octalLiteral</span><span> </span></span><span>=</span><span> </span><span>484</span><span>;</span></div></div><div><div><div>7</div></div><div><span>var</span><span><span> </span><span>notANumber</span><span> </span></span><span>=</span><span> </span><span>NaN</span><span>;</span></div></div><div><div><div>8</div></div><div><span>var</span><span><span> </span><span>infinityNumber</span><span> </span></span><span>=</span><span> </span><span>Infinity</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中 <code>0b1010</code> 和 <code>0o744</code> 是 ES6 中的二进制和八进制表示法，它们会被编译为十进制数字。</p></section><section><h2>字符串<a href="#字符串"><span>#</span></a></h2><p>使用 <code>string</code> 定义字符串类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myName</span></span><span>:</span><span> </span><span>string</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>myAge</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>25</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 模板字符串</span></div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>sentence</span></span><span>:</span><span> </span><span>string</span><span> </span><span>=</span><span> </span><span>`Hello, my name is </span><span>${</span><span>myName</span><span>}</span><span>.</span></div></div><div><div><div>6</div></div><div><span>I'll be </span><span>${</span><span>myAge</span><span> </span><span>+</span><span> </span><span>1</span><span>}</span><span> years old next month.`</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>编译结果：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>myName</span><span> </span></span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>myAge</span><span> </span></span><span>=</span><span> </span><span>25</span><span>;</span></div></div><div><div><div>3</div></div><div><span>// 模板字符串</span></div></div><div><div><div>4</div></div><div><span>var</span><span><span> </span><span>sentence</span><span> </span></span><span>=</span><span> </span><span>"Hello, my name is "</span><span> </span><span>+</span><span><span> </span><span>myName</span><span> </span></span><span>+</span><span> </span><span>".</span><span>\n</span><span>I'll be "</span><span> </span><span>+</span><span><span> (</span><span>myAge</span><span> </span></span><span>+</span><span> </span><span>1</span><span>) </span><span>+</span><span> </span><span>" years old next month."</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>空值<a href="#空值"><span>#</span></a></h2><p>JavaScript 没有空值（Void）的概念，在 TypeScript 中，可以用 <code>void</code> 表示没有任何返回值的函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>alertName</span><span>()</span><span>:</span><span> </span><span>void</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>alert</span><span>(</span><span>'My name is Tom'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>声明一个 <code>void</code> 类型的变量没有什么用，因为你只能将它赋值为 <code>undefined</code> 和 <code>null</code>（只在 —strictNullChecks 未指定时）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>unusable</span></span><span>:</span><span> </span><span>void</span><span> </span><span>=</span><span> </span><span>undefined</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>Null 和 Undefined<a href="#null-和-undefined"><span>#</span></a></h2><p>在 TypeScript 中，可以使用 <code>null</code> 和 <code>undefined</code> 来定义这两个原始数据类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>u</span></span><span>:</span><span> </span><span>undefined</span><span> </span><span>=</span><span> </span><span>undefined</span><span>;</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>n</span></span><span>:</span><span> </span><span>null</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>与 <code>void</code> 的区别是，<code>undefined</code> 和 <code>null</code> 是所有类型的子类型。也就是说 <code>undefined</code> 类型的变量，可以赋值给 <code>number</code> 类型的变量：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 这样不会报错</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>num</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>undefined</span><span>;</span></div></div><div><div><div>3</div></div><div><span>// 这样也不会报错</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>u</span></span><span>:</span><span> </span><span>undefined</span><span>;</span></div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>num</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span><span> </span><span>u</span><span>;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>而 <code>void</code> 类型的变量不能赋值给 <code>number</code> 类型的变量：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>u</span></span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>num</span></span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span><span> </span><span>u</span><span>;</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// Type 'void' is not assignable to type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>任意值<a href="#任意值"><span>#</span></a></h1><p>任意值（Any）用来表示允许赋值为任意类型。</p><section><h2>什么是任意值类型<a href="#什么是任意值类型"><span>#</span></a></h2><p>如果是一个普通类型，在赋值过程中改变类型是不被允许的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span></span><span>:</span><span> </span><span>string</span><span> </span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但如果是 <code>any</code> 类型，则允许被赋值为任意类型。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span></span><span>:</span><span> </span><span>any</span><span> </span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>任意值的属性和方法<a href="#任意值的属性和方法"><span>#</span></a></h2><p>在任意值上访问任何属性都是允许的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>anyThing</span></span><span>:</span><span> </span><span>any</span><span> </span><span>=</span><span> </span><span>'hello'</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>anyThing</span><span>.</span></span><span>myName</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>anyThing</span><span>.</span></span><span>myName</span><span>.</span><span>firstName</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也允许调用任何方法：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>anyThing</span></span><span>:</span><span> </span><span>any</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>anyThing</span><span>.</span></span><span>setName</span><span>(</span><span>'Jerry'</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>anyThing</span><span>.</span></span><span>setName</span><span>(</span><span>'Jerry'</span><span>).</span><span>sayHello</span><span>();</span></div></div><div><div><div>4</div></div><div><span><span>anyThing</span><span>.</span></span><span>myName</span><span>.</span><span>setFirstName</span><span>(</span><span>'Cat'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以认为，<strong>声明一个变量为任意值之后，对它的任何操作，返回的内容的类型都是任意值</strong>。</p></section><section><h2>未声明类型的变量<a href="#未声明类型的变量"><span>#</span></a></h2><p><strong>变量如果在声明的时候，未指定其类型，那么它会被识别为任意值类型</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>something</span><span>;</span></span></div></div><div><div><div>2</div></div><div><span><span>something</span><span> </span></span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>something</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>something</span><span>.</span></span><span>setName</span><span>(</span><span>'Tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>等价于</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>something</span></span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>something</span><span> </span></span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>something</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>something</span><span>.</span></span><span>setName</span><span>(</span><span>'Tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>类型推论<a href="#类型推论"><span>#</span></a></h1><p>如果没有明确的指定类型，那么 TypeScript 会依照类型推论（Type Inference）的规则推断出一个类型。</p><section><h2>什么是类型推论<a href="#什么是类型推论"><span>#</span></a></h2><p>以下代码虽然没有指定类型，但是会在编译的时候报错：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>事实上，它等价于：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span></span><span>:</span><span> </span><span>string</span><span> </span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>TypeScript 会在没有明确的指定类型的时候推测出一个类型，这就是类型推论。</p><p><strong>如果定义的时候没有赋值，不管之后有没有赋值，都会被推断成 <code>any</code> 类型而完全不被类型检查</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span><span>;</span></span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>联合类型<a href="#联合类型"><span>#</span></a></h1><p>联合类型（Union Types）表示取值可以为多种类型中的一种。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span></span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span></span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.</span></div></div><div><div><div>8</div></div><div><span>//   Type 'boolean' is not assignable to type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>联合类型使用 <code>|</code> 分隔每个类型。</p><p>这里的 <code>let myFavoriteNumber: string | number</code> 的含义是，允许 <code>myFavoriteNumber</code> 的类型是 <code>string</code> 或者 <code>number</code>，但是不能是其他类型。</p><section><h2>访问联合类型的属性或方法<a href="#访问联合类型的属性或方法"><span>#</span></a></h2><p>当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候，我们<strong>只能访问此联合类型的所有类型里共有的属性或方法</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getLength</span><span><span>(</span><span>something</span></span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>something</span><span>.</span></span><span>length</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.</span></div></div><div><div><div>6</div></div><div><span>//   Property 'length' does not exist on type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，<code>length</code> 不是 <code>string</code> 和 <code>number</code> 的共有属性，所以会报错。</p><p>访问 <code>string</code> 和 <code>number</code> 的共有属性是没问题的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getString</span><span><span>(</span><span>something</span></span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>string</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>something</span><span>.</span></span><span>toString</span><span>();</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>联合类型的变量在被赋值的时候，会根据类型推论的规则推断出一个类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>myFavoriteNumber</span></span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>'seven'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myFavoriteNumber</span><span>.</span></span><span>length</span><span>); </span><span>// 5</span></div></div><div><div><div>4</div></div><div><span><span>myFavoriteNumber</span><span> </span></span><span>=</span><span> </span><span>7</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myFavoriteNumber</span><span>.</span></span><span>length</span><span>); </span><span>// 编译时报错</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，第二行的 <code>myFavoriteNumber</code> 被推断成了 <code>string</code>，访问它的 <code>length</code> 属性不会报错。</p><p>而第四行的 <code>myFavoriteNumber</code> 被推断成了 <code>number</code>，访问它的 <code>length</code> 属性时就报错了。</p></section></section>
<section><h1>接口<a href="#接口"><span>#</span></a></h1><p>在 TypeScript 中，我们使用接口（Interfaces）来定义对象的类型。</p><section><h2>什么是接口<a href="#什么是接口"><span>#</span></a></h2><p>在面向对象语言中，接口（Interfaces）是一个很重要的概念，它是对行为的抽象，而具体如何行动需要由类（classes）去实现（implement）。</p><p>TypeScript 中的接口是一个非常灵活的概念，除了可用于对类的一部分行为进行抽象以外，也常用于对「对象的形状（Shape）」进行描述。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>25</span></div></div><div><div><div>9</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，我们定义了一个接口 <code>Person</code>，接着定义了一个变量 <code>tom</code>，它的类型是 <code>Person</code>。这样，我们就约束了 <code>tom</code> 的形状必须和接口 <code>Person</code> 一致。</p><p>接口一般首字母大写。</p><p>定义的变量比接口少了一些属性是不允许的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// index.ts(6,5): error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.</span></div></div><div><div><div>11</div></div><div><span>//   Property 'age' is missing in type '{ name: string; }'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>多一些属性也是不允许的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>25</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>10</div></div><div><span>};</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// index.ts(9,5): error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.</span></div></div><div><div><div>13</div></div><div><span>//   Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可见，<strong>赋值的时候，变量的形状必须和接口的形状保持一致</strong>。</p></section><section><h2>可选属性<a href="#可选属性"><span>#</span></a></h2><p>有时我们希望不要完全匹配一个形状，那么可以用可选属性：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div><div><div><div>9</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>25</span></div></div><div><div><div>17</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>可选属性的含义是该属性可以不存在。</p><p>这时<strong>仍然不允许添加未定义的属性</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>25</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>10</div></div><div><span>};</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// examples/playground/index.ts(9,5): error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.</span></div></div><div><div><div>13</div></div><div><span>//   Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>任意属性<a href="#任意属性"><span>#</span></a></h2><p>有时候我们希望一个接口允许有任意的属性，可以使用如下方式：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>propName</span><span>:</span><span> </span><span>string</span><span>]</span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>10</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用 <code>[propName: string]</code> 定义了任意属性取 <code>string</code> 类型的值。</p><p>需要注意的是，<strong>一旦定义了任意属性，那么确定属性和可选属性的类型都必须是它的类型的子集</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>propName</span><span>:</span><span> </span><span>string</span><span>]</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>25</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// index.ts(3,5): error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.</span></div></div><div><div><div>14</div></div><div><span>// index.ts(7,5): error TS2322: Type '{ [x: string]: string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'.</span></div></div><div><div><div>15</div></div><div><span>//   Index signatures are incompatible.</span></div></div><div><div><div>16</div></div><div><span>//     Type 'string | number' is not assignable to type 'string'.</span></div></div><div><div><div>17</div></div><div><span>//       Type 'number' is not assignable to type 'string'.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上例中，任意属性的值允许是 <code>string</code>，但是可选属性 <code>age</code> 的值却是 <code>number</code>，<code>number</code> 不是 <code>string</code> 的子属性，所以报错了。</p><p>另外，在报错信息中可以看出，此时 <code>{ name: 'Tom', age: 25, gender: 'male' }</code> 的类型被推断成了 <code>{ [x: string]: string | number; name: string; age: number; gender: string; }</code>，这是联合类型和接口的结合。</p><p>一个接口中只能定义一个任意属性。如果接口中有多个类型的属性，则可以在任意属性中使用联合类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>propName</span><span>:</span><span> </span><span>string</span><span>]</span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>25</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 类型“number | undefined”的属性“age”不能赋给“string”索引类型“string | number”。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然而<code>age</code> 是可选属性，<code>age</code> 的值可能为 <code>undefined</code> 所以正确的写法应该是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>propName</span><span>:</span><span> </span><span>string</span><span>]</span><span>:</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>undefined</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>只读属性<a href="#只读属性"><span>#</span></a></h2><p>有时候我们希望对象中的一些字段只能在创建的时候被赋值，那么可以用 <code>readonly</code> 定义只读属性：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>readonly</span><span><span> </span><span>id</span></span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>[</span><span>propName</span><span>:</span><span> </span><span>string</span><span>]</span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>id</span><span>:</span><span> </span><span>89757</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>11</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>12</div></div><div><span>};</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>tom</span><span>.</span></span><span>id</span><span> </span><span>=</span><span> </span><span>9527</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上例中，使用 <code>readonly</code> 定义的属性 <code>id</code> 初始化后，又被赋值了，所以报错了。</p><p><strong>注意，只读的约束存在于第一次给对象赋值的时候，而不是第一次给只读属性赋值的时候</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Person</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>readonly</span><span><span> </span><span>id</span></span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>age</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>[</span><span>propName</span><span>:</span><span> </span><span>string</span><span>]</span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Person</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>gender</span><span>:</span><span> </span><span>'male'</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>tom</span><span>.</span></span><span>id</span><span> </span><span>=</span><span> </span><span>89757</span><span>;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// index.ts(8,5): error TS2322: Type '{ name: string; gender: string; }' is not assignable to type 'Person'.</span></div></div><div><div><div>16</div></div><div><span>//   Property 'id' is missing in type '{ name: string; gender: string; }'.</span></div></div><div><div><div>17</div></div><div><span>// index.ts(13,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上例中，报错信息有两处，第一处是在对 <code>tom</code> 进行赋值的时候，没有给 <code>id</code> 赋值。</p><p>第二处是在给 <code>tom.id</code> 赋值的时候，由于它是只读属性，所以报错了。</p></section></section>
<section><h1>数组的类型<a href="#数组的类型"><span>#</span></a></h1><p>在 TypeScript 中，数组类型有多种定义方式，比较灵活。</p><section><h2>「类型 + 方括号」表示法<a href="#类型--方括号表示法"><span>#</span></a></h2><p>最简单的方法是使用「类型 + 方括号」来表示数组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>fibonacci</span></span><span>:</span><span> </span><span>number</span><span>[] </span><span>=</span><span> [</span><span>1</span><span>, </span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>5</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>数组的项中<strong>不允许</strong>出现其他的类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>fibonacci</span></span><span>:</span><span> </span><span>number</span><span>[] </span><span>=</span><span> [</span><span>1</span><span>, </span><span>'1'</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>5</span><span>];</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// Type 'string' is not assignable to type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>数组的一些方法的参数也会根据数组在定义时约定的类型进行限制：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>fibonacci</span></span><span>:</span><span> </span><span>number</span><span>[] </span><span>=</span><span> [</span><span>1</span><span>, </span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>5</span><span>];</span></div></div><div><div><div>2</div></div><div><span><span>fibonacci</span><span>.</span></span><span>push</span><span>(</span><span>'8'</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// Argument of type '"8"' is not assignable to parameter of type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，<code>push</code> 方法只允许传入 <code>number</code> 类型的参数，但是却传了一个 <code>"8"</code> 类型的参数，所以报错了。这里 <code>"8"</code> 是一个字符串字面量类型，会在后续章节中详细介绍。</p></section><section><h2>数组泛型<a href="#数组泛型"><span>#</span></a></h2><p>我们也可以使用数组泛型（Array Generic） <code>Array&lt;elemType&gt;</code> 来表示数组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>fibonacci</span></span><span>:</span><span> </span><span>Array</span><span><span>&lt;</span><span>number</span><span>&gt; </span></span><span>=</span><span> [</span><span>1</span><span>, </span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>5</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>用接口表示数组<a href="#用接口表示数组"><span>#</span></a></h2><p>接口也可以用来描述数组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>NumberArray</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>[</span><span>index</span><span>:</span><span> </span><span>number</span><span>]</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>fibonacci</span></span><span>:</span><span> </span><span>NumberArray</span><span> </span><span>=</span><span> [</span><span>1</span><span>, </span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>5</span><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>NumberArray</code> 表示：只要索引的类型是数字时，那么值的类型必须是数字。</p><p>虽然接口也可以用来描述数组，但是我们一般不会这么做，因为这种方式比前两种方式复杂多了。</p><p>不过有一种情况例外，那就是它常用来表示类数组。</p></section><section><h2>类数组<a href="#类数组"><span>#</span></a></h2><p>类数组（Array-like Object）不是数组类型，比如 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments" target="_blank">arguments</a>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>sum</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>args</span></span><span>:</span><span> </span><span>number</span><span>[] </span><span>=</span><span> </span><span>arguments</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，<code>arguments</code> 实际上是一个类数组，它包含了函数运行时的所有参数。虽然它看起来像数组，但它没有数组的高阶方法（如 <code>map</code>, <code>forEach</code>）。不能用普通的数组的方式来描述，而应该用接口：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>sum</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>args</span></span><span>:</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>[</span><span>index</span><span>:</span><span> </span><span>number</span><span>]</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>length</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>5</div></div><div><span>        </span><span>callee</span><span>:</span><span> </span><span>Function</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>} </span><span>=</span><span> </span><span>arguments</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在这个例子中，我们除了约束当索引的类型是数字时，值的类型必须是数字之外，也约束了它还有 <code>length</code> 和 <code>callee</code> 两个属性。</p><blockquote><p><code>[index: number]: number</code></p><ul>
<li>这行代码告诉 TypeScript：“这个对象可以通过数字索引来访问（比如 <code>args[0]</code>, <code>args[1]</code>），并且访问到的值必须是 <code>number</code> 类型。”</li>
<li>由于 <code>arguments</code> 存储的是传入函数的参数，这里显式规定了所有参数都必须是数字。</li>
</ul><p><code>length: number</code></p><ul>
<li>类数组对象必须具备的属性，表示传入参数的数量。</li>
</ul><p><code>callee: Function</code></p><ul>
<li>这是一个指向当前正在执行的函数的引用。</li>
<li><strong>注意：</strong> 在现代 JavaScript 的“严格模式”（Strict Mode）下，访问 <code>arguments.callee</code> 会报错。在 TypeScript 中保留它通常是为了兼容老旧代码或特定的底层逻辑。</li>
</ul></blockquote><p>事实上常用的类数组都有自己的接口定义，如 <code>IArguments</code>, <code>NodeList</code>, <code>HTMLCollection</code> 等：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>sum</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>args</span></span><span>:</span><span> </span><span>IArguments</span><span> </span><span>=</span><span> </span><span>arguments</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中 <code>IArguments</code> 是 TypeScript 中定义好了的类型，它实际上就是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>IArguments</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>[</span><span>index</span><span>:</span><span> </span><span>number</span><span>]</span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>length</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>callee</span><span>:</span><span> </span><span>Function</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们现在很少直接使用 <code>arguments</code> 对象，因为它不够直观且难以优化。更推荐使用 <strong>剩余参数（Rest Parameters）</strong>，这样 TypeScript 可以自动推导类型，且代码更简洁：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 使用剩余参数，args 直接就是一个真正的数组</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>sum</span><span>(</span><span>...</span><span>args</span><span>:</span><span> </span><span>number</span><span>[]) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>// args 此时是 number[] 类型，可以直接使用 reduce 等数组方法</span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span><span> </span><span>args</span><span>.</span></span><span>reduce</span><span><span>((</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span><span>, </span></span><span>0</span><span>);</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>any 在数组中的应用<a href="#any-在数组中的应用"><span>#</span></a></h2><p>一个比较常见的做法是，用 <code>any</code> 表示数组中允许出现任意类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>list</span></span><span>:</span><span> </span><span>any</span><span>[] </span><span>=</span><span> [</span><span>'xcatliu'</span><span>, </span><span>25</span><span>, { </span><span>website</span><span>:</span><span> </span><span>'http://xcatliu.com'</span><span> }];</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>函数的类型<a href="#函数的类型"><span>#</span></a></h1><section><h2>函数声明<a href="#函数声明"><span>#</span></a></h2><p>在 JavaScript 中，有两种常见的定义函数的方式——函数声明（Function Declaration）和函数表达式（Function Expression）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 函数声明（Function Declaration）</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>sum</span><span><span>(</span><span>x</span><span>, </span><span>y</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 函数表达式（Function Expression）</span></div></div><div><div><div>7</div></div><div><span>let</span><span> </span><span>mySum</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>x</span><span>, </span><span>y</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>9</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>一个函数有输入和输出，要在 TypeScript 中对其进行约束，需要把输入和输出都考虑到，其中函数声明的类型定义较简单：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>sum</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，<strong>输入多余的（或者少于要求的）参数，是不被允许的</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>sum</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>sum</span><span>(</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.</span></div></div><div><div><div>7</div></div><div><span>function</span><span> </span><span>sum</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div><span>sum</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>函数表达式<a href="#函数表达式"><span>#</span></a></h2><p>如果要我们现在写一个函数表达式（Function Expression）的定义，可能会写成这样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span> </span><span>mySum</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这是可以通过编译的，不过事实上，上面的代码只对等号右侧的匿名函数进行了类型定义，而等号左边的 <code>mySum</code>，是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 <code>mySum</code> 添加类型，则应该是这样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span> </span><span>mySum</span><span>:</span><span><span> (</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>) </span><span>=&gt;</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意不要混淆了 TypeScript 中的 <code>=&gt;</code> 和 ES6 中的 <code>=&gt;</code>。</p><p>在 TypeScript 的类型定义中，<code>=&gt;</code> 用来表示函数的定义，左边是输入类型，需要用括号括起来，右边是输出类型。</p><p>在 ES6 中，<code>=&gt;</code> 叫做箭头函数。</p></section><section><h2>用接口定义函数的形状<a href="#用接口定义函数的形状"><span>#</span></a></h2><p>我们也可以使用接口的方式来定义一个函数需要符合的形状：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>SearchFunc</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>(</span><span>source</span><span>:</span><span> </span><span>string</span><span><span>, </span><span>subString</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>boolean</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>mySearch</span></span><span>:</span><span> </span><span>SearchFunc</span><span>;</span></div></div><div><div><div>6</div></div><div><span>mySearch</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>source</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>subString</span></span><span>:</span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>source</span><span>.</span></span><span>search</span><span><span>(</span><span>subString</span><span>) </span></span><span>!==</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>采用函数表达式|接口定义函数的方式时，对等号左侧进行类型限制，可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。</p></section><section><h2>可选参数<a href="#可选参数"><span>#</span></a></h2><p>前面提到，输入多余的（或者少于要求的）参数，是不允许的。那么如何定义可选的参数呢？</p><p>与接口中的可选属性类似，我们用 <code>?</code> 表示可选的参数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>buildName</span><span><span>(</span><span>firstName</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>lastName</span></span><span><span>?</span><span>:</span></span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span><span> (</span><span>lastName</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span><span> </span><span>firstName</span><span> </span></span><span>+</span><span> </span><span>' '</span><span> </span><span>+</span><span><span> </span><span>lastName</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>return</span><span><span> </span><span>firstName</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>tomcat</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>'Tom'</span><span>, </span><span>'Cat'</span><span>);</span></div></div><div><div><div>9</div></div><div><span>let</span><span><span> </span><span>tom</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>'Tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>需要注意的是，可选参数必须接在必需参数后面。换句话说，<strong>可选参数后面不允许再出现必需参数了</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>buildName</span><span><span>(</span><span>firstName</span></span><span><span>?</span><span>:</span></span><span> </span><span>string</span><span><span>, </span><span>lastName</span></span><span>:</span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span><span> (</span><span>firstName</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span><span> </span><span>firstName</span><span> </span></span><span>+</span><span> </span><span>' '</span><span> </span><span>+</span><span><span> </span><span>lastName</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>return</span><span><span> </span><span>lastName</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>let</span><span><span> </span><span>tomcat</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>'Tom'</span><span>, </span><span>'Cat'</span><span>);</span></div></div><div><div><div>9</div></div><div><span>let</span><span><span> </span><span>tom</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>undefined</span><span>, </span><span>'Tom'</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>参数默认值<a href="#参数默认值"><span>#</span></a></h2><p>在 ES6 中，我们允许给函数的参数添加默认值，<strong>TypeScript 会将添加了默认值的参数识别为可选参数</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>buildName</span><span><span>(</span><span>firstName</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>lastName</span></span><span>:</span><span> </span><span>string</span><span> </span><span>=</span><span> </span><span>'Cat'</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>firstName</span><span> </span></span><span>+</span><span> </span><span>' '</span><span> </span><span>+</span><span><span> </span><span>lastName</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>tomcat</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>'Tom'</span><span>, </span><span>'Cat'</span><span>);</span></div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>tom</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>'Tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>此时就不受「可选参数必须接在必需参数后面」的限制了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>buildName</span><span><span>(</span><span>firstName</span></span><span>:</span><span> </span><span>string</span><span> </span><span>=</span><span> </span><span>'Tom'</span><span><span>, </span><span>lastName</span></span><span>:</span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>firstName</span><span> </span></span><span>+</span><span> </span><span>' '</span><span> </span><span>+</span><span><span> </span><span>lastName</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>tomcat</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>'Tom'</span><span>, </span><span>'Cat'</span><span>);</span></div></div><div><div><div>5</div></div><div><span>let</span><span><span> </span><span>cat</span><span> </span></span><span>=</span><span> </span><span>buildName</span><span>(</span><span>undefined</span><span>, </span><span>'Cat'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>剩余参数<a href="#剩余参数"><span>#</span></a></h2><p>ES6 中，可以使用 <code>...rest</code> 的方式获取函数中的剩余参数（rest 参数）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>push</span><span><span>(</span><span>array</span><span>, </span></span><span>...</span><span><span>items</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>items</span><span>.</span><span>forEach</span><span>(</span><span>function</span><span><span>(</span><span>item</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>array</span><span>.</span><span>push</span><span><span>(</span><span>item</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>a</span></span><span>:</span><span> </span><span>any</span><span>[] </span><span>=</span><span> [];</span></div></div><div><div><div>8</div></div><div><span>push</span><span><span>(</span><span>a</span><span>, </span></span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>事实上，<code>items</code> 是一个数组。所以我们可以用数组的类型来定义它：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>push</span><span><span>(</span><span>array</span></span><span>:</span><span> </span><span>any</span><span>[], </span><span>...</span><span>items</span><span>:</span><span> </span><span>any</span><span>[]) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>items</span><span>.</span><span>forEach</span><span>(</span><span>function</span><span><span>(</span><span>item</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>array</span><span>.</span><span>push</span><span><span>(</span><span>item</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> [];</span></div></div><div><div><div>8</div></div><div><span>push</span><span><span>(</span><span>a</span><span>, </span></span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，rest 参数只能是最后一个参数。</p></section><section><h2>重载<a href="#重载"><span>#</span></a></h2><p>重载允许一个函数接受不同数量或类型的参数时，作出不同的处理。</p><p>比如，我们需要实现一个函数 <code>reverse</code>，输入数字 <code>123</code> 的时候，输出反转的数字 <code>321</code>，输入字符串 <code>'hello'</code> 的时候，输出反转的字符串 <code>'olleh'</code>。</p><p>利用联合类型，我们可以这么实现：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>void</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>'number'</span><span>) {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span> </span><span>Number</span><span><span>(</span><span>x</span><span>.</span></span><span>toString</span><span>().</span><span>split</span><span>(</span><span>''</span><span>).</span><span>reverse</span><span>().</span><span>join</span><span>(</span><span>''</span><span>));</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>'string'</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>return</span><span><span> </span><span>x</span><span>.</span></span><span>split</span><span>(</span><span>''</span><span>).</span><span>reverse</span><span>().</span><span>join</span><span>(</span><span>''</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>然而这样有一个缺点，就是不能够精确的表达，输入为数字的时候，输出也应该为数字，输入为字符串的时候，输出也应该为字符串。</strong></p><p>这时，我们可以使用重载定义多个 <code>reverse</code> 的函数类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>function</span><span> </span><span>reverse</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>number</span><span> </span><span>|</span><span> </span><span>string</span><span> </span><span>|</span><span> </span><span>void</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>'number'</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>return</span><span> </span><span>Number</span><span><span>(</span><span>x</span><span>.</span></span><span>toString</span><span>().</span><span>split</span><span>(</span><span>''</span><span>).</span><span>reverse</span><span>().</span><span>join</span><span>(</span><span>''</span><span>));</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>'string'</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>return</span><span><span> </span><span>x</span><span>.</span></span><span>split</span><span>(</span><span>''</span><span>).</span><span>reverse</span><span>().</span><span>join</span><span>(</span><span>''</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上例中，我们重复定义了多次函数 <code>reverse</code>，前几次都是函数定义，最后一次是函数实现。在编辑器的代码提示中，可以正确的看到前两个提示。</p><p>注意，TypeScript 会优先从最前面的函数定义开始匹配，所以多个函数定义如果有包含关系，需要优先把精确的定义写在前面。</p></section></section>
<section><h1>类型断言<a href="#类型断言"><span>#</span></a></h1><p>类型断言（Type Assertion）可以用来手动指定一个值的类型。</p><section><h2>语法<a href="#语法"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>值</span><span> </span><span>as</span><span> </span></span><span>类型</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>类型</span><span><span>&gt;</span><span>值</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 tsx 语法（React 的 jsx 语法的 ts 版）中必须使用前者，即 <code>值 as 类型</code>。</p><p>形如 <code>&lt;Foo&gt;</code> 的语法在 tsx 中表示的是一个 <code>ReactNode</code>，在 ts 中除了表示类型断言之外，也可能是表示一个泛型。</p><p>故在使用类型断言时，统一使用 <code>值 as 类型</code> 这样的语法。</p></section><section><h2>类型断言的用途<a href="#类型断言的用途"><span>#</span></a></h2><p>类型断言的常见用途有以下几种：</p><section><h3>将一个联合类型断言为其中一个类型<a href="#将一个联合类型断言为其中一个类型"><span>#</span></a></h3><p>当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候，我们<strong>只能访问此联合类型的所有类型中共有的属性或方法</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Fish</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>swim</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>getName</span><span><span>(</span><span>animal</span></span><span>:</span><span> </span><span>Cat</span><span> </span><span>|</span><span> </span><span>Fish</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</span><span><span> </span><span>animal</span><span>.</span></span><span>name</span><span>;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>而有时候，我们确实需要在还不确定类型的时候就访问其中一个类型特有的属性或方法，比如：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Fish</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>swim</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>isFish</span><span><span>(</span><span>animal</span></span><span>:</span><span> </span><span>Cat</span><span> </span><span>|</span><span> </span><span>Fish</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>animal</span><span>.</span></span><span>swim</span><span> </span><span>===</span><span> </span><span>'function'</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// index.ts:11:23 - error TS2339: Property 'swim' does not exist on type 'Cat | Fish'.</span></div></div><div><div><div>18</div></div><div><span>//   Property 'swim' does not exist on type 'Cat'.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面的例子中，获取 <code>animal.swim</code> 的时候会报错。</p><p>此时可以使用类型断言，将 <code>animal</code> 断言成 <code>Fish</code>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Fish</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>swim</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>isFish</span><span><span>(</span><span>animal</span></span><span>:</span><span> </span><span>Cat</span><span> </span><span>|</span><span> </span><span>Fish</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> (</span><span>animal</span><span> </span><span>as</span><span> </span></span><span>Fish</span><span>).</span><span>swim</span><span> </span><span>===</span><span> </span><span>'function'</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这样就可以解决访问 <code>animal.swim</code> 时报错的问题了。</p><p>需要注意的是，类型断言只能够「欺骗」TypeScript 编译器，无法避免运行时的错误，反而滥用类型断言可能会导致运行时错误：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Fish</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>swim</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>swim</span><span><span>(</span><span>animal</span></span><span>:</span><span> </span><span>Cat</span><span> </span><span>|</span><span> </span><span>Fish</span><span>) {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>(</span><span>animal</span><span> </span><span>as</span><span> </span><span>Fish</span><span>).</span><span>swim</span><span>();</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>const</span><span> </span><span>tom</span><span>:</span><span> </span><span>Cat</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>    </span><span>run</span><span><span>() { </span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'run'</span><span>) }</span></div></div><div><div><div>17</div></div><div><span>};</span></div></div><div><div><div>18</div></div><div><span>swim</span><span><span>(</span><span>tom</span><span>);</span></span></div></div><div><div><div>19</div></div><div><span>// Uncaught TypeError: animal.swim is not a function`</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面的例子编译时不会报错，但在运行时会报错：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Uncaught TypeError: animal.swim is not a function`</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>原因是 <code>(animal as Fish).swim()</code> 这段代码隐藏了 <code>animal</code> 可能为 <code>Cat</code> 的情况，将 <code>animal</code> 直接断言为 <code>Fish</code> 了，而 TypeScript 编译器信任了我们的断言，故在调用 <code>swim()</code> 时没有编译错误。</p><p>可是 <code>swim</code> 函数接受的参数是 <code>Cat | Fish</code>，一旦传入的参数是 <code>Cat</code> 类型的变量，由于 <code>Cat</code> 上没有 <code>swim</code> 方法，就会导致运行时错误了。</p><p>总之，使用类型断言时一定要格外小心，尽量避免断言后调用方法或引用深层属性，以减少不必要的运行时错误。</p></section><section><h3>将一个父类断言为更加具体的子类<a href="#将一个父类断言为更加具体的子类"><span>#</span></a></h3><p>当类之间有继承关系时，类型断言也是很常见的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>ApiError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>code</span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>class</span><span> </span><span>HttpError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>statusCode</span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>200</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>function</span><span> </span><span>isApiError</span><span><span>(</span><span>error</span></span><span>:</span><span> </span><span>Error</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> (</span><span>error</span><span> </span><span>as</span><span> </span></span><span>ApiError</span><span>).</span><span>code</span><span> </span><span>===</span><span> </span><span>'number'</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，我们声明了函数 <code>isApiError</code>，它用来判断传入的参数是不是 <code>ApiError</code> 类型，为了实现这样一个函数，它的参数的类型肯定得是比较抽象的父类 <code>Error</code>，这样的话这个函数就能接受 <code>Error</code> 或它的子类作为参数了。</p><p>但是由于父类 <code>Error</code> 中没有 <code>code</code> 属性，故直接获取 <code>error.code</code> 会报错，需要使用类型断言获取 <code>(error as ApiError).code</code>。</p><p>大家可能会注意到，在这个例子中有一个更合适的方式来判断是不是 <code>ApiError</code>，那就是使用 <code>instanceof</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>ApiError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>code</span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>class</span><span> </span><span>HttpError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>statusCode</span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>200</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>function</span><span> </span><span>isApiError</span><span><span>(</span><span>error</span></span><span>:</span><span> </span><span>Error</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span><span> (</span><span>error</span><span> </span></span><span>instanceof</span><span> </span><span>ApiError</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，确实使用 <code>instanceof</code> 更加合适，因为 <code>ApiError</code> 是一个 JavaScript 的类，能够通过 <code>instanceof</code> 来判断 <code>error</code> 是否是它的实例。</p><p>但是有的情况下 <code>ApiError</code> 和 <code>HttpError</code> 不是一个真正的类，而只是一个 TypeScript 的接口（<code>interface</code>），接口是一个类型，不是一个真正的值，它在编译结果中会被删除，当然就无法使用 <code>instanceof</code> 来做运行时判断了：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>ApiError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>code</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>HttpError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>statusCode</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>function</span><span> </span><span>isApiError</span><span><span>(</span><span>error</span></span><span>:</span><span> </span><span>Error</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span><span> (</span><span>error</span><span> </span></span><span>instanceof</span><span> </span><span>ApiError</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// index.ts:9:26 - error TS2693: 'ApiError' only refers to a type, but is being used as a value here.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>此时就只能用类型断言，通过判断是否存在 <code>code</code> 属性，来判断传入的参数是不是 <code>ApiError</code> 了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>ApiError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>code</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>HttpError</span><span> </span><span>extends</span><span> </span><span>Error</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>statusCode</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>function</span><span> </span><span>isApiError</span><span><span>(</span><span>error</span></span><span>:</span><span> </span><span>Error</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> (</span><span>error</span><span> </span><span>as</span><span> </span></span><span>ApiError</span><span>).</span><span>code</span><span> </span><span>===</span><span> </span><span>'number'</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>将任何一个类型断言为 <code>any</code><a href="#将任何一个类型断言为-any"><span>#</span></a></h3><p>理想情况下，TypeScript 的类型系统运转良好，每个值的类型都具体而精确。</p><p>当我们引用一个在此类型上不存在的属性或方法时，就会报错：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>foo</span><span>:</span><span> </span><span>number</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>foo</span><span>.</span></span><span>length</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// index.ts:2:5 - error TS2339: Property 'length' does not exist on type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，数字类型的变量 <code>foo</code> 上是没有 <code>length</code> 属性的，故 TypeScript 给出了相应的错误提示。</p><p>这种错误提示显然是非常有用的。</p><p>但有的时候，我们非常确定这段代码不会出错，比如下面这个例子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>window</span><span>.</span></span><span>foo</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// index.ts:1:8 - error TS2339: Property 'foo' does not exist on type 'Window &amp; typeof globalThis'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，我们需要将 <code>window</code> 上添加一个属性 <code>foo</code>，但 TypeScript 编译时会报错，提示我们 <code>window</code> 上不存在 <code>foo</code> 属性。</p><p>此时我们可以使用 <code>as any</code> 临时将 <code>window</code> 断言为 <code>any</code> 类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>(</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>foo</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>any</code> 类型的变量上，访问任何属性都是允许的。</p><p>需要注意的是，将一个变量断言为 <code>any</code> 可以说是解决 TypeScript 中类型问题的最后一个手段。</p><p><strong>它极有可能掩盖了真正的类型错误，所以如果不是非常确定，就不要使用 <code>as any</code>。</strong></p><p>上面的例子中，我们也可以通过扩展 window 的类型解决这个错误，不过如果只是临时的增加 <code>foo</code> 属性，<code>as any</code> 会更加方便。</p><p>总之，<strong>一方面不能滥用 <code>as any</code>，另一方面也不要完全否定它的作用，我们需要在类型的严格性和开发的便利性之间掌握平衡</strong>（这也是 <a href="https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals" target="_blank">TypeScript 的设计理念</a>之一），才能发挥出 TypeScript 最大的价值。</p></section><section><h3>将 <code>any</code> 断言为一个具体的类型<a href="#将-any-断言为一个具体的类型"><span>#</span></a></h3><p>在日常的开发中，我们不可避免的需要处理 <code>any</code> 类型的变量，它们可能是由于第三方库未能定义好自己的类型，也有可能是历史遗留的或其他人编写的烂代码，还可能是受到 TypeScript 类型系统的限制而无法精确定义类型的场景。</p><p>遇到 <code>any</code> 类型的变量时，我们可以选择无视它，任由它滋生更多的 <code>any</code>。</p><p>我们也可以选择改进它，通过类型断言及时的把 <code>any</code> 断言为精确的类型，亡羊补牢，使我们的代码向着高可维护性的目标发展。</p><p>举例来说，历史遗留的代码中有个 <code>getCacheData</code>，它的返回值是 <code>any</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getCacheData</span><span><span>(</span><span>key</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>any</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> (</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>cache</span><span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>那么我们在使用它时，最好能够将调用了它之后的返回值断言成一个精确的类型，这样就方便了后续的操作：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getCacheData</span><span><span>(</span><span>key</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>any</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> (</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>cache</span><span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>tom</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>(</span><span>'tom'</span><span><span>) </span><span>as</span><span> </span></span><span>Cat</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>tom</span><span>.</span></span><span>run</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，我们调用完 <code>getCacheData</code> 之后，立即将它断言为 <code>Cat</code> 类型。这样的话明确了 <code>tom</code> 的类型，后续对 <code>tom</code> 的访问时就有了代码补全，提高了代码的可维护性。</p></section></section><section><h2>类型断言的限制<a href="#类型断言的限制"><span>#</span></a></h2><p>从上面的例子中，我们可以总结出：</p><ul>
<li>联合类型可以被断言为其中一个类型</li>
<li>父类可以被断言为子类</li>
<li>任何类型都可以被断言为 any</li>
<li>any 可以被断言为任何类型</li>
</ul><p>那么类型断言有没有什么限制呢？是不是任何一个类型都可以被断言为任何另一个类型呢？</p><p>答案是否定的——并不是任何一个类型都可以被断言为任何另一个类型。</p><p>具体来说，若 <code>A</code> 兼容 <code>B</code>，那么 <code>A</code> 能够被断言为 <code>B</code>，<code>B</code> 也能被断言为 <code>A</code>。</p><p>下面我们通过一个简化的例子，来理解类型断言的限制：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Cat</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>11</div></div><div><span>    </span><span>run</span><span>:</span><span> () </span><span>=&gt;</span><span><span> { </span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'run'</span><span>) }</span></div></div><div><div><div>12</div></div><div><span>};</span></div></div><div><div><div>13</div></div><div><span>let</span><span><span> </span><span>animal</span></span><span>:</span><span> </span><span>Animal</span><span> </span><span>=</span><span><span> </span><span>tom</span><span>;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>TypeScript 是结构类型系统，类型之间的对比只会比较它们最终的结构，而会忽略它们定义时的关系。</p><p>在上面的例子中，<code>Cat</code> 包含了 <code>Animal</code> 中的所有属性，除此之外，它还有一个额外的方法 <code>run</code>。TypeScript 并不关心 <code>Cat</code> 和 <code>Animal</code> 之间定义时是什么关系，而只会看它们最终的结构有什么关系——所以它与 <code>Cat extends Animal</code> 是等价的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Cat</span><span> </span><span>extends</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>那么也不难理解为什么 <code>Cat</code> 类型的 <code>tom</code> 可以赋值给 <code>Animal</code> 类型的 <code>animal</code> 了——就像面向对象编程中我们可以将子类的实例赋值给类型为父类的变量。</p><p>我们把它换成 TypeScript 中更专业的说法，即：<code>Animal</code> 兼容 <code>Cat</code>。</p><p>当 <code>Animal</code> 兼容 <code>Cat</code> 时，它们就可以互相进行类型断言了：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>function</span><span> </span><span>testAnimal</span><span><span>(</span><span>animal</span></span><span>:</span><span> </span><span>Animal</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span><span> (</span><span>animal</span><span> </span><span>as</span><span> </span></span><span>Cat</span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div><span>function</span><span> </span><span>testCat</span><span><span>(</span><span>cat</span></span><span>:</span><span> </span><span>Cat</span><span>) {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>return</span><span><span> (</span><span>cat</span><span> </span><span>as</span><span> </span></span><span>Animal</span><span>);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样的设计其实也很容易就能理解：</p><ul>
<li>允许 <code>animal as Cat</code> 是因为「父类可以被断言为子类」，这个前面已经学习过了</li>
<li>允许 <code>cat as Animal</code> 是因为既然子类拥有父类的属性和方法，那么被断言为父类，获取父类的属性、调用父类的方法，就不会有任何问题，故「子类可以被断言为父类」</li>
</ul><p>总之，若 <code>A</code> 兼容 <code>B</code>，那么 <code>A</code> 能够被断言为 <code>B</code>，<code>B</code> 也能被断言为 <code>A</code>。</p><p>同理，若 <code>B</code> 兼容 <code>A</code>，那么 <code>A</code> 能够被断言为 <code>B</code>，<code>B</code> 也能被断言为 <code>A</code>。</p><p>所以这也可以换一种说法：</p><p>要使得 <code>A</code> 能够被断言为 <code>B</code>，只需要 <code>A</code> 兼容 <code>B</code> 或 <code>B</code> 兼容 <code>A</code> 即可，这也是为了在类型断言时的安全考虑，毕竟毫无根据的断言是非常危险的。</p><p>综上所述：</p><ul>
<li>联合类型可以被断言为其中一个类型</li>
<li>父类可以被断言为子类</li>
<li>任何类型都可以被断言为 any</li>
<li>any 可以被断言为任何类型</li>
<li>要使得 <code>A</code> 能够被断言为 <code>B</code>，只需要 <code>A</code> 兼容 <code>B</code> 或 <code>B</code> 兼容 <code>A</code> 即可</li>
</ul><p>其实前四种情况都是最后一个的特例。</p></section><section><h2>双重断言<a href="#双重断言"><span>#</span></a></h2><p>既然：</p><ul>
<li>任何类型都可以被断言为 any</li>
<li>any 可以被断言为任何类型</li>
</ul><p>那么我们是不是可以使用双重断言 <code>as any as Foo</code> 来将任何一个类型断言为任何另一个类型呢？</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Fish</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>swim</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>function</span><span> </span><span>testCat</span><span><span>(</span><span>cat</span></span><span>:</span><span> </span><span>Cat</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>return</span><span><span> (</span><span>cat</span><span> </span><span>as</span><span> </span></span><span>any</span><span><span> </span><span>as</span><span> </span></span><span>Fish</span><span>);</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在上面的例子中，若直接使用 <code>cat as Fish</code> 肯定会报错，因为 <code>Cat</code> 和 <code>Fish</code> 互相都不兼容。</p><p>但是若使用双重断言，则可以打破「要使得 <code>A</code> 能够被断言为 <code>B</code>，只需要 <code>A</code> 兼容 <code>B</code> 或 <code>B</code> 兼容 <code>A</code> 即可」的限制，将任何一个类型断言为任何另一个类型。</p><p>若使用了这种双重断言，那么十有八九是非常错误的，它很可能会导致运行时错误。</p><p><strong>除非迫不得已，千万别用双重断言。</strong></p></section><section><h2>类型断言 vs 类型转换<a href="#类型断言-vs-类型转换"><span>#</span></a></h2><p>类型断言只会影响 TypeScript 编译时的类型，类型断言语句在编译结果中会被删除：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>toBoolean</span><span><span>(</span><span>something</span></span><span>:</span><span> </span><span>any</span><span>)</span><span>:</span><span> </span><span>boolean</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>something</span><span> </span><span>as</span><span> </span></span><span>boolean</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>toBoolean</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>6</div></div><div><span>// 返回值为 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在上面的例子中，将 <code>something</code> 断言为 <code>boolean</code> 虽然可以通过编译，但是并没有什么用，代码在编译后会变成：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>toBoolean</span><span><span>(</span><span>something</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> </span><span>something</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>toBoolean</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>6</div></div><div><span>// 返回值为 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>所以类型断言不是类型转换，它不会真的影响到变量的类型。</p><p>若要进行类型转换，需要直接调用类型转换的方法：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>toBoolean</span><span><span>(</span><span>something</span></span><span>:</span><span> </span><span>any</span><span>)</span><span>:</span><span> </span><span>boolean</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span> </span><span>Boolean</span><span><span>(</span><span>something</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>toBoolean</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>6</div></div><div><span>// 返回值为 true</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>类型断言 vs 类型声明<a href="#类型断言-vs-类型声明"><span>#</span></a></h2><p>在这个例子中：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getCacheData</span><span><span>(</span><span>key</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>any</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> (</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>cache</span><span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>tom</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>(</span><span>'tom'</span><span><span>) </span><span>as</span><span> </span></span><span>Cat</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>tom</span><span>.</span></span><span>run</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们使用 <code>as Cat</code> 将 <code>any</code> 类型断言为了 <code>Cat</code> 类型。</p><p>但实际上还有其他方式可以解决这个问题：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getCacheData</span><span><span>(</span><span>key</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>any</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> (</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>cache</span><span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>tom</span><span>:</span><span> </span><span>Cat</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>(</span><span>'tom'</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>tom</span><span>.</span></span><span>run</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，我们通过类型声明的方式，将 <code>tom</code> 声明为 <code>Cat</code>，然后再将 <code>any</code> 类型的 <code>getCacheData('tom')</code> 赋值给 <code>Cat</code> 类型的 <code>tom</code>。</p><p>这和类型断言是非常相似的，而且产生的结果也几乎是一样的——<code>tom</code> 在接下来的代码中都变成了 <code>Cat</code> 类型。</p><p>它们的区别，可以通过这个例子来理解：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>const</span><span> </span><span>animal</span><span>:</span><span> </span><span>Animal</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'tom'</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div><span>let</span><span><span> </span><span>tom</span><span> </span></span><span>=</span><span><span> </span><span>animal</span><span> </span><span>as</span><span> </span></span><span>Cat</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在上面的例子中，由于 <code>Animal</code> 兼容 <code>Cat</code>，故可以将 <code>animal</code> 断言为 <code>Cat</code> 赋值给 <code>tom</code>。</p><p>但是若直接声明 <code>tom</code> 为 <code>Cat</code> 类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>const</span><span> </span><span>animal</span><span>:</span><span> </span><span>Animal</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'tom'</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div><span>let</span><span><span> </span><span>tom</span></span><span>:</span><span> </span><span>Cat</span><span> </span><span>=</span><span><span> </span><span>animal</span><span>;</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// index.ts:12:5 - error TS2741: Property 'run' is missing in type 'Animal' but required in type 'Cat'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>则会报错，不允许将 <code>animal</code> 赋值为 <code>Cat</code> 类型的 <code>tom</code>。</p><p>这很容易理解，<code>Animal</code> 可以看作是 <code>Cat</code> 的父类，当然不能将父类的实例赋值给类型为子类的变量。</p><p>深入的讲，它们的核心区别就在于：</p><ul>
<li><code>animal</code> 断言为 <code>Cat</code>，只需要满足 <code>Animal</code> 兼容 <code>Cat</code> 或 <code>Cat</code> 兼容 <code>Animal</code> 即可</li>
<li><code>animal</code> 赋值给 <code>tom</code>，需要满足 <code>Cat</code> 兼容 <code>Animal</code> 才行</li>
</ul><p>但是 <code>Cat</code> 并不兼容 <code>Animal</code>。</p><p>而在前一个例子中，由于 <code>getCacheData('tom')</code> 是 <code>any</code> 类型，<code>any</code> 兼容 <code>Cat</code>，<code>Cat</code> 也兼容 <code>any</code>，故</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>tom</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>(</span><span>'tom'</span><span><span>) </span><span>as</span><span> </span></span><span>Cat</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>等价于</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>tom</span><span>:</span><span> </span><span>Cat</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>(</span><span>'tom'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>知道了它们的核心区别，就知道了类型声明是比类型断言更加严格的。</p><p>所以为了增加代码的质量，我们最好优先使用类型声明，这也比类型断言的 <code>as</code> 语法更加优雅。</p></section><section><h2>类型断言 vs 泛型<a href="#类型断言-vs-泛型"><span>#</span></a></h2><p>还是这个例子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getCacheData</span><span><span>(</span><span>key</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>any</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> (</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>cache</span><span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>tom</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>(</span><span>'tom'</span><span><span>) </span><span>as</span><span> </span></span><span>Cat</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>tom</span><span>.</span></span><span>run</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们还有第三种方式可以解决这个问题，那就是泛型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>getCacheData</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>key</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span><span> (</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>cache</span><span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>interface</span><span> </span><span>Cat</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>run</span><span>()</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>tom</span><span> </span><span>=</span><span> </span><span>getCacheData</span><span>&lt;</span><span>Cat</span><span>&gt;(</span><span>'tom'</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>tom</span><span>.</span></span><span>run</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>通过给 <code>getCacheData</code> 函数添加了一个泛型 <code>&lt;T&gt;</code>，我们可以更加规范的实现对 <code>getCacheData</code> 返回值的约束，这也同时去除掉了代码中的 <code>any</code>，是最优的一个解决方案。</p></section></section>
<section><h1>内置对象<a href="#内置对象"><span>#</span></a></h1><p>JavaScript 中有很多<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects" target="_blank">内置对象</a>，它们可以直接在 TypeScript 中当做定义好了的类型。</p><p>内置对象是指根据标准在全局作用域（Global）上存在的对象。这里的标准是指 ECMAScript 和其他环境（比如 DOM）的标准。</p><section><h2>ECMAScript 的内置对象<a href="#ecmascript-的内置对象"><span>#</span></a></h2><p>ECMAScript 标准提供的内置对象有：</p><p><code>Boolean</code>、<code>Error</code>、<code>Date</code>、<code>RegExp</code> 等。</p><p>我们可以在 TypeScript 中将变量定义为这些类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>b</span></span><span>:</span><span> </span><span>Boolean</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Boolean</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>e</span></span><span>:</span><span> </span><span>Error</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'Error occurred'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>d</span></span><span>:</span><span> </span><span>Date</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Date</span><span>();</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>r</span></span><span>:</span><span> </span><span>RegExp</span><span> </span><span>=</span><span> /</span><span>[a-z]</span><span>/</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>更多的内置对象，可以查看 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects" target="_blank">MDN 的文档</a>。</p><p>而他们的定义文件，则在 <a href="https://github.com/Microsoft/TypeScript/tree/master/src/lib" target="_blank">TypeScript 核心库的定义文件</a>中。</p></section><section><h2>DOM 和 BOM 的内置对象<a href="#dom-和-bom-的内置对象"><span>#</span></a></h2><p>DOM 和 BOM 提供的内置对象有：</p><p><code>Document</code>、<code>HTMLElement</code>、<code>Event</code>、<code>NodeList</code> 等。</p><p>TypeScript 中会经常用到这些类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>body</span></span><span>:</span><span> </span><span>HTMLElement</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>body</span><span>;</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>allDiv</span></span><span>:</span><span> </span><span>NodeList</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelectorAll</span><span>(</span><span>'div'</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>document</span><span>.</span></span><span>addEventListener</span><span>(</span><span>'click'</span><span>, </span><span>function</span><span><span>(</span><span>e</span></span><span>:</span><span> </span><span>MouseEvent</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// Do something</span></div></div><div><div><div>5</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它们的定义文件同样在 <a href="https://github.com/Microsoft/TypeScript/tree/master/src/lib" target="_blank">TypeScript 核心库的定义文件</a>中。</p></section><section><h2>TypeScript 核心库的定义文件<a href="#typescript-核心库的定义文件"><span>#</span></a></h2><p><a href="https://github.com/Microsoft/TypeScript/tree/master/src/lib" target="_blank">TypeScript 核心库的定义文件</a>中定义了所有浏览器环境需要用到的类型，并且是预置在 TypeScript 中的。</p><p>当你在使用一些常用的方法的时候，TypeScript 实际上已经帮你做了很多类型判断的工作了，比如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>Math</span><span>.</span></span><span>pow</span><span>(</span><span>10</span><span>, </span><span>'2'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// index.ts(1,14): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，<code>Math.pow</code> 必须接受两个 <code>number</code> 类型的参数。事实上 <code>Math.pow</code> 的类型定义如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Math</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>/**</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>* Returns the value of a base expression taken to a specified power.</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>x</span><span> The base value of the expression.</span></div></div><div><div><div>5</div></div><div><span><span>     </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>y</span><span> The exponent value of the expression.</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>    </span><span>pow</span><span><span>(</span><span>x</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>y</span></span><span>:</span><span> </span><span>number</span><span>)</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>再举一个 DOM 中的例子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>document</span><span>.</span></span><span>addEventListener</span><span>(</span><span>'click'</span><span>, </span><span>function</span><span><span>(</span><span>e</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>e</span><span>.</span></span><span>targetCurrent</span><span>);</span></div></div><div><div><div>3</div></div><div><span>});</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// index.ts(2,17): error TS2339: Property 'targetCurrent' does not exist on type 'MouseEvent'.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子中，<code>addEventListener</code> 方法是在 TypeScript 核心库中定义的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface</span><span> </span><span>Document</span><span> </span><span>extends</span><span> </span><span>Node</span><span>, </span><span>GlobalEventHandlers</span><span>, </span><span>NodeSelector</span><span>, </span><span>DocumentEvent</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>addEventListener</span><span><span>(</span><span>type</span></span><span>:</span><span> </span><span>string</span><span>, </span><span>listener</span><span>:</span><span><span> (</span><span>ev</span></span><span>:</span><span> </span><span>MouseEvent</span><span>) </span><span>=&gt;</span><span> </span><span>any</span><span><span>, </span><span>useCapture</span></span><span><span>?</span><span>:</span></span><span> </span><span>boolean</span><span>)</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>所以 <code>e</code> 被推断成了 <code>MouseEvent</code>，而 <code>MouseEvent</code> 是没有 <code>targetCurrent</code> 属性的，所以报错了。</p><p>注意，TypeScript 核心库的定义中不包含 Node.js 部分。</p></section><section><h2>用 TypeScript 写 Node.js<a href="#用-typescript-写-nodejs"><span>#</span></a></h2><p>Node.js 不是内置对象的一部分，如果想用 TypeScript 写 Node.js，则需要引入第三方声明文件：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>@types/node</span><span> </span><span>--save-dev</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/state/</id>
      <title type="text">state</title>
      <published>2026-01-18T01:47:40.000Z</published>
      <updated>2026-01-18T01:47:40.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/state/"/>
      <summary type="text">组件通常需要根据交互更改屏幕上显示的内容。输入表单应该更新输入字段，单击轮播图上的“下一个”应该更改显示的图片，单击“购买”应该将商品放入购物车。组件需要“记住”某些东西：当前输入值、当前图片、购物车。在 React 中，这种组件特有的记忆被称为 state 。</summary>
      <content type="html"><![CDATA[<p>组件通常需要根据交互更改屏幕上显示的内容。输入表单应该更新输入字段，单击轮播图上的“下一个”应该更改显示的图片，单击“购买”应该将商品放入购物车。组件需要“记住”某些东西：当前输入值、当前图片、购物车。在 React 中，这种组件特有的记忆被称为 <strong>state</strong>。</p>
<section><h2>为什么要使用 state<a href="#为什么要使用-state"><span>#</span></a></h2><p>props 是来自组件外部的数据，是无法更改的，如果一个组件需要保存自己的数据该怎么办？并随着时间保持它？或者是让我们的应用程序具有交互性，由于某个操作而更改 UI ?</p><p>这就是 state 的作用， 基本上就是一个组件的数据可以随着时间的推移而保持，我们由 state 来获取数据。可以认为 state 作为组件的内存，随着组件的创建而创建，随着组件的卸载而销毁。</p><p>用户产生的每一条数据都是一个 state ，每当更新 state 时，react 都会重新渲染，生成新的组件视图，到用户界面上就是更改 UI 。</p><p>以下是一个渲染雕塑图片的组件。点击 “Next” 按钮应该显示下一个雕塑并将 <code>index</code> 更改为 <code>1</code>，再次点击又更改为 <code>2</code>，以此类推。但这个组件现在<strong>不起作用</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>sculptureList</span><span> } </span><span>from</span><span> </span><span>'./data.js'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Gallery</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>let</span><span> </span><span>index</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>index</span><span> </span><span>=</span><span> </span><span>index</span><span> </span><span>+</span><span> </span><span>1</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>  </span><span>let</span><span> </span><span>sculpture</span><span> </span><span>=</span><span> </span><span>sculptureList</span><span>[</span><span>index</span><span>]</span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span></span><span>handleClick</span><span><span>}</span><span>&gt;Next&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>&lt;</span><span>h2</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>&lt;</span><span>i</span><span>&gt;</span><span>{</span><span><span>sculpture</span><span>.</span></span><span>name</span><span>}</span><span> &lt;/</span><span>i</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>by </span><span>{</span><span><span>sculpture</span><span>.</span></span><span>artist</span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>&lt;</span><span>h3</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>(</span><span>{</span><span>index</span><span> </span><span>+</span><span> </span><span>1</span><span>}</span><span> of </span><span>{</span><span><span>sculptureList</span><span>.</span></span><span>length</span><span>}</span><span>)</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>&lt;/</span><span>h3</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>handleClick()</code> 事件处理函数正在更新局部变量 <code>index</code>。但存在两个原因使得变化不可见：</p><ol>
<li><strong>局部变量无法在多次渲染中持久保存。</strong> 当 React 再次渲染这个组件时，它会从头开始渲染——不会考虑之前对局部变量的任何更改。</li>
<li><strong>更改局部变量不会触发渲染。</strong> React 没有意识到它需要使用新数据再次渲染组件。</li>
</ol><p>要使用新数据更新组件，需要做两件事：</p><ol>
<li><strong>保留</strong> 渲染之间的数据。</li>
<li><strong>触发</strong> React 使用新数据渲染组件（重新渲染）。</li>
</ol><p><a href="https://zh-hans.react.dev/reference/react/useState" target="_blank"><code>useState</code></a> Hook 提供了这两个功能：</p><ol>
<li><strong>State 变量</strong> 用于保存渲染间的数据。</li>
<li><strong>State setter 函数</strong> 更新变量并触发 React 再次渲染组件。</li>
</ol><blockquote><p>Hooks ——以 <code>use</code> 开头的函数——只能在组件或<a href="https://zh-hans.react.dev/learn/reusing-logic-with-custom-hooks" target="_blank">自定义 Hook</a> 的最顶层调用。你不能在条件语句、循环语句或其他嵌套函数内调用 Hook。Hook 是函数，但将它们视为关于组件需求的无条件声明会很有帮助。在组件顶部 “use” React 特性，类似于在文件顶部“导入”模块。</p></blockquote></section>
<section><h2>添加一个 state 变量<a href="#添加一个-state-变量"><span>#</span></a></h2><p>要添加 state 变量，先从文件顶部的 React 中导入 <code>useState</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>将<code>let index = 0</code>修改为</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>index</span><span>, </span><span>setIndex</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>index</code> 是一个 state 变量，<code>setIndex</code> 是对应的 setter 函数。</p><blockquote><p>这里的 <code>[</code> 和 <code>]</code> 语法称为<a href="https://zh.javascript.info/destructuring-assignment" target="_blank">数组解构</a>，它允许你从数组中读取值。 <code>useState</code> 返回的数组总是正好有两项。</p></blockquote><p>以下展示了它们在 <code>handleClick()</code> 中是如何共同起作用的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setIndex</span><span><span>(</span><span>index</span><span> </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2><code>useState</code><a href="#usestate"><span>#</span></a></h2><p>当你调用 <a href="https://zh-hans.react.dev/reference/react/useState" target="_blank"><code>useState</code></a> 时，你是在告诉 React 你想让这个组件记住一些东西：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>index</span><span>, </span><span>setIndex</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在这个例子里，你希望 React 记住 <code>index</code>。</p><p><code>useState</code> 的唯一参数是 state 变量的<strong>初始值</strong>。在这个例子中，<code>index</code> 的初始值被<code>useState(0)</code>设置为 <code>0</code>。</p><p>每次你的组件渲染时，<code>useState</code> 都会给你一个包含两个值的数组：</p><ol>
<li><strong>state 变量</strong> (<code>index</code>) 会保存上次渲染的值。</li>
<li><strong>state setter 函数</strong> (<code>setIndex</code>) 可以更新 state 变量并触发 React 重新渲染组件。</li>
</ol><p>以下是实际发生的情况：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>index</span><span>, </span><span>setIndex</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>组件进行第一次渲染。</strong> 因为你将 <code>0</code> 作为 <code>index</code> 的初始值传递给 <code>useState</code>，它将返回 <code>[0, setIndex]</code>。 React 记住 <code>0</code> 是最新的 state 值。</li>
<li><strong>你更新了 state</strong>。当用户点击按钮时，它会调用 <code>setIndex(index + 1)</code>。 <code>index</code> 是 <code>0</code>，所以它是 <code>setIndex(1)</code>。这告诉 React 现在记住 <code>index</code> 是 <code>1</code> 并触发下一次渲染。</li>
<li><strong>组件进行第二次渲染</strong>。React 仍然看到 <code>useState(0)</code>，但是因为 React <em>记住</em> 了你将 <code>index</code> 设置为了 <code>1</code>，它将返回 <code>[1, setIndex]</code>。</li>
<li>以此类推！</li>
</ol><p><strong>在同一组件的每次渲染中，Hooks 都依托于一个稳定的调用顺序</strong>。这在实践中很有效，因为如果你遵循上面的规则（“只在顶层调用 Hooks”），Hooks 将始终以相同的顺序被调用。</p><p>在 React 内部，为每个组件保存了一个数组，其中每一项都是一个 state 对。它维护当前 state 对的索引值，在渲染之前将其设置为 “0”。每次调用 useState 时，React 都会为你提供一个 state 对并增加索引值。</p><p>一个简单的 useState 实现：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>componentHooks</span><span> </span></span><span>=</span><span> []</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>currentHookIndex</span><span> </span></span><span>=</span><span> </span><span>0</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// useState 在 React 中是如何工作的（简化版）</span></div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>useState</span><span><span>(</span><span>initialState</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>let</span><span><span> </span><span>pair</span><span> </span></span><span>=</span><span><span> </span><span>componentHooks</span><span>[</span><span>currentHookIndex</span><span>]</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>if</span><span><span> (</span><span>pair</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>// 这不是第一次渲染</span></div></div><div><div><div>9</div></div><div><span>    </span><span>// 所以 state pair 已经存在</span></div></div><div><div><div>10</div></div><div><span>    </span><span>// 将其返回并为下一次 hook 的调用做准备</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>currentHookIndex</span><span>++</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span><span> </span><span>pair</span></span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>  </span><span>// 这是我们第一次进行渲染</span></div></div><div><div><div>16</div></div><div><span>  </span><span>// 所以新建一个 state pair 然后存储它</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>pair</span><span> </span><span>=</span><span><span> [</span><span>initialState</span><span>, </span><span>setState</span><span>]</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>  </span><span>function</span><span> </span><span>setState</span><span><span>(</span><span>nextState</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>// 当用户发起 state 的变更，</span></div></div><div><div><div>21</div></div><div><span>    </span><span>// 把新的值放入 pair 中</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>pair</span><span>[</span><span>0</span><span>] </span><span>=</span><span><span> </span><span>nextState</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>updateDOM</span><span>()</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>  </span><span>// 存储这个 pair 用于将来的渲染</span></div></div><div><div><div>27</div></div><div><span>  </span><span>// 并且为下一次 hook 的调用做准备</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>componentHooks</span><span>[</span><span>currentHookIndex</span><span>] </span><span>=</span><span><span> </span><span>pair</span></span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>currentHookIndex</span><span>++</span></div></div><div><div><div>30</div></div><div><span>  </span><span>return</span><span><span> </span><span>pair</span></span></div></div><div><div><div>31</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>State 是隔离且私有的<a href="#state-是隔离且私有的"><span>#</span></a></h2><p>State 是屏幕上组件实例内部的状态。换句话说，<strong>如果你渲染同一个组件两次，每个副本都会有完全隔离的 state</strong>！改变其中一个不会影响另一个。</p><p>在这个例子中， <code>Gallery</code> 组件以同样的逻辑被渲染了两次。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Gallery</span><span> </span><span>from</span><span> </span><span>'./Gallery.js'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Page</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>className</span><span>=</span><span>'Page'</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>&lt;</span><span>Gallery</span><span> /&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;</span><span>Gallery</span><span> /&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这就是 state 与声明在模块顶部的普通变量不同的原因。 State 不依赖于特定的函数调用或在代码中的位置，它的作用域“只限于”屏幕上的某块特定区域。你渲染了两个 <code>&lt;Gallery /&gt;</code> 组件，所以它们的 state 是分别存储的。</p><p>还要注意 <code>Page</code> 组件“不知道”关于 <code>Gallery</code> state 的任何信息，甚至不知道它是否有任何 state。与 props 不同，<strong>state 完全私有于声明它的组件</strong>。父组件无法更改它。这使你可以向任何组件添加或删除 state，而不会影响其他组件。</p><p>如果你希望两个画廊保持其 states 同步怎么办？在 React 中执行此操作的正确方法是从子组件中<em>删除</em> state 并将其添加到离它们最近的共享父组件中。</p><blockquote><ul>
<li>当一个组件需要在多次渲染间“记住”某些信息时使用 state 变量。</li>
<li>State 变量是通过调用 <code>useState</code> Hook 来声明的。</li>
<li>Hook 是以 <code>use</code> 开头的特殊函数。它们能让你 “hook” 到像 state 这样的 React 特性中。</li>
<li>Hook 可能会让你想起 import：它们需要在非条件语句中调用。调用 Hook 时，包括 <code>useState</code>，仅在组件或另一个 Hook 的顶层被调用才有效。</li>
<li><code>useState</code> Hook 返回一对值：当前 state 和更新它的函数。</li>
<li>你可以拥有多个 state 变量。在内部，React 按顺序匹配它们。</li>
<li>State 是组件私有的。如果你在两个地方渲染它，则每个副本都有独属于自己的 state。</li>
</ul></blockquote></section>
<section><h2>state 如同一张快照<a href="#state-如同一张快照"><span>#</span></a></h2><section><h3>设置 state 会触发渲染<a href="#设置-state-会触发渲染"><span>#</span></a></h3><p>你可能会认为你的用户界面会直接对点击之类的用户输入做出响应并发生变化。在 React 中，它的工作方式与这种思维模型略有不同。在上一页中，你看到了来自 React 的<a href="https://zh-hans.react.dev/learn/render-and-commit#step-1-trigger-a-render" target="_blank">设置 state 请求重新渲染</a>。这意味着要使界面对输入做出反应，你需要设置其 state。</p><p>在这个例子中，当你按下 “send” 时，<code>setIsSent(true)</code> 会通知 React 重新渲染 UI：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Form</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>isSent</span><span>, </span><span>setIsSent</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>false</span><span>)</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> [</span><span>message</span><span>, </span><span>setMessage</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>'Hi!'</span><span>)</span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span> (</span><span>isSent</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> &lt;</span><span>h1</span><span>&gt;Your message is on its way!&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;</span><span>form</span></div></div><div><div><div>11</div></div><div><span>      </span><span>onSubmit</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>e</span><span>.</span><span>preventDefault</span><span>()</span></div></div><div><div><div>13</div></div><div><span>        </span><span>setIsSent</span><span>(</span><span>true</span><span>)</span></div></div><div><div><div>14</div></div><div><span>        </span><span>sendMessage</span><span>(</span><span>message</span><span>)</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>&lt;</span><span>textarea</span><span> </span><span>placeholder</span><span>=</span><span>'Message'</span><span> </span><span>value</span><span><span>=</span><span>{</span></span><span>message</span><span><span>}</span><span> </span></span><span>onChange</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> </span><span>setMessage</span><span><span>(</span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>value</span><span><span>)</span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>type</span><span>=</span><span>'submit'</span><span>&gt;Send&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>&lt;/</span><span>form</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>function</span><span> </span><span>sendMessage</span><span><span>(</span><span>message</span><span>) {</span></span></div></div><div><div><div>23</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>当单击按钮时会发生以下情况：</p><ol>
<li>执行 <code>onSubmit</code> 事件处理函数。</li>
<li><code>setIsSent(true)</code> 将 <code>isSent</code> 设置为 <code>true</code> 并排列一个新的渲染。</li>
<li>React 根据新的 <code>isSent</code> 值重新渲染组件。</li>
</ol><p><a href="https://zh-hans.react.dev/learn/render-and-commit#step-2-react-renders-your-components" target="_blank">“正在渲染”</a> 就意味着 React 正在调用你的组件——一个函数。你从该函数返回的 JSX 就像是在某个时间点上 UI 的快照。它的 props、事件处理函数和内部变量都是 <strong>根据当前渲染时的 state</strong> 被计算出来的。</p></section><section><h3>渲染会及时生成一张快照<a href="#渲染会及时生成一张快照"><span>#</span></a></h3><p>与照片或电影画面不同，你返回的 UI “快照”是可交互的。它其中包括类似事件处理函数的逻辑，这些逻辑用于指定如何对输入作出响应。React 随后会更新屏幕来匹配这张快照，并绑定事件处理函数。因此，按下按钮就会触发你 JSX 中的点击事件处理函数。</p><p>当 React 重新渲染一个组件时：</p><ol>
<li>React 会再次调用你的函数</li>
<li>函数会返回新的 JSX 快照</li>
<li>React 会更新界面以匹配返回的快照</li>
</ol><p>state 不同于在你的函数返回之后就会消失的普通变量。state 实际上“活”在 React 本身中——就像被摆在一个架子上！——位于你的函数之外。当 React 调用你的组件时，它会为特定的那一次渲染提供一张 state 快照。你的组件会在其 JSX 中返回一张包含一整套新的 props 和事件处理函数的 UI 快照 ，其中所有的值都是 <strong>根据那一次渲染中 state 的值</strong> 被计算出来的！</p><p>在这个例子中，你可能会以为点击“+3”按钮会调用 <code>setNumber(number + 1)</code> 三次从而使计数器递增三次。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Counter</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>number</span><span>, </span><span>setNumber</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;</span><span>{</span><span>number</span><span>}</span><span>&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;+3&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>每次点击只会让 <code>number</code> 递增一次！</p><p><strong>设置 state 只会为下一次渲染变更 state 的值</strong>。在第一次渲染期间，<code>number</code> 为 <code>0</code>。这也就解释了为什么在 <strong>那次渲染中的</strong> <code>onClick</code> 处理函数中，即便在调用了 <code>setNumber(number + 1)</code> 之后，<code>number</code> 的值也仍然是 <code>0</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;button onClick={() =&gt; {</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>setNumber(number + 1);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>setNumber(number + 1);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>setNumber(number + 1);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>}}&gt;+3&lt;/button&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>以下是这个按钮的点击事件处理函数通知 React 要做的事情：</p><ol>
<li><code>setNumber(number + 1)</code>：<code>number</code> 是  <code>0</code> 所以 <code>setNumber(0 + 1)</code>。
<ul>
<li>React 准备在下一次渲染时将 <code>number</code> 更改为 <code>1</code>。</li>
</ul>
</li>
<li><code>setNumber(number + 1)</code>：<code>number</code> 是  <code>0</code> 所以 <code>setNumber(0 + 1)</code>。
<ul>
<li>React 准备在下一次渲染时将 <code>number</code> 更改为 <code>1</code>。</li>
</ul>
</li>
<li><code>setNumber(number + 1)</code>：<code>number</code> 是  <code>0</code> 所以 <code>setNumber(0 + 1)</code>。
<ul>
<li>React 准备在下一次渲染时将 <code>number</code> 更改为 <code>1</code>。</li>
</ul>
</li>
</ol><p>尽管你调用了三次 <code>setNumber(number + 1)</code>，但在 <strong>这次渲染的</strong> 事件处理函数中 <code>number</code> 会一直是 <code>0</code>，所以你会三次将 state 设置成 <code>1</code>。这就是为什么在你的事件处理函数执行完以后，React 重新渲染的组件中的 <code>number</code> 等于 <code>1</code> 而不是 <code>3</code>。</p><p>你还可以通过在心里把 state 变量替换成它们在你代码中的值来想象这个过程。由于 <strong>这次渲染</strong> 中的 state 变量 <code>number</code> 是 <code>0</code>，其事件处理函数看起来会像这样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>0</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>0</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>4</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>0</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>}</span><span>}</span><span>&gt;+3&lt;/</span></span><span>button</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对于下一次渲染来说，<code>number</code> 是 <code>1</code>，因此 <strong>那次渲染中的</strong> 点击事件处理函数看起来会像这样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>1</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>1</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>4</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>1</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>}</span><span>}</span><span>&gt;+3&lt;/</span></span><span>button</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这就是为什么再次点击按钮会将计数器设置为 <code>2</code>，下次点击时会设为 <code>3</code>，依此类推。</p></section><section><h3>异步事件中的 state<a href="#异步事件中的-state"><span>#</span></a></h3><p>在下边的例子中，给<code>alert</code> 添加一个定时器，使得它在组件重新渲染<strong>之后</strong>才触发，又会怎样呢？是会显示 “0” 还是 “5” ？</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Counter</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>number</span><span>, </span><span>setNumber</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;</span><span>{</span><span>number</span><span>}</span><span>&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>5</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>          </span><span>alert</span><span>(</span><span>number</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>}, </span><span>3000</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;+5&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>当你点击 <code>button</code> 时：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setNumber</span><span>(</span><span>0</span><span> </span><span>+</span><span> </span><span>5</span><span>);</span></div></div><div><div><div>2</div></div><div><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>alert</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>4</div></div><div><span>}, </span><span>3000</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>到提示框运行时，React 中存储的 state 可能已经发生了更改，但它是使用用户与之交互时状态的快照进行调度的！</p><p><strong>一个 state 变量的值永远不会在一次渲染的内部发生变化，</strong> 即使其事件处理函数的代码是异步的。在 <strong>那次渲染的</strong> <code>onClick</code> 内部，<code>number</code> 的值即使在调用 <code>setNumber(number + 5)</code> 之后也还是 <code>0</code>。它的值在 React 通过调用你的组件“获取 UI 的快照”时就被“固定”了。</p><p><strong>React 会使 state 的值始终“固定”在一次渲染的各个事件处理函数内部</strong>。你无需担心代码运行时 state 是否发生了变化。</p></section></section>
<section><h2>把 state 更新加入队列<a href="#把-state-更新加入队列"><span>#</span></a></h2><p>设置组件 state 会把一次重新渲染加入队列。但有时你可能会希望在下次渲染加入队列之前对 state 的值执行多次操作。</p><section><h3>React 会对 state 更新进行批处理<a href="#react-会对-state-更新进行批处理"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Counter</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> [</span><span>number</span><span>, </span><span>setNumber</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;</span><span>{</span><span>number</span><span>}</span><span>&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>9</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>10</div></div><div><span>        </span><span>setNumber</span><span>(</span><span>number</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;+3&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>每一次渲染的 state 值都是固定的，因此无论你调用多少次 <code>setNumber(1)</code>，在第一次渲染的事件处理函数内部的 <code>number</code> 值总是 <code>0</code> ：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setNumber</span><span>(</span><span>0</span><span> </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>2</div></div><div><span>setNumber</span><span>(</span><span>0</span><span> </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>3</div></div><div><span>setNumber</span><span>(</span><span>0</span><span> </span><span>+</span><span> </span><span>1</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是这里还有另外一个影响因素需要讨论。<strong>React 会等到事件处理函数中的</strong> 所有 <strong>代码都运行完毕再处理 state 更新。</strong> 这就是重新渲染只会发生在所有这些 <code>setNumber()</code> 调用 <strong>之后</strong> 的原因。</p><p>这让你可以更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的 <a href="https://zh-hans.react.dev/learn/render-and-commit#re-renders-when-state-updates" target="_blank">重新渲染</a>。但这也意味着只有在你的事件处理函数及其中任何代码执行完成 <strong>之后</strong>，UI 才会更新。这种特性也就是 <strong>批处理</strong></p></section><section><h3>在下次渲染前多次更新同一个 state<a href="#在下次渲染前多次更新同一个-state"><span>#</span></a></h3><p>这是一个不常见的用例，但是如果你想在下次渲染之前多次更新同一个 state，你可以像 <code>setNumber(n =&gt; n + 1)</code> 这样传入一个根据队列中的前一个 state 计算下一个 state 的 <strong>函数</strong>，而不是像 <code>setNumber(number + 1)</code> 这样传入 <strong>下一个 state 值</strong>。这是一种告诉 React “用 state 值做某事”而不是仅仅替换它的方法。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Counter</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>number</span><span>, </span><span>setNumber</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;</span><span>{</span><span>number</span><span>}</span><span>&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>setNumber</span><span><span>(</span><span>n</span><span> </span></span><span>=&gt;</span><span> </span><span>n</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>setNumber</span><span><span>(</span><span>n</span><span> </span></span><span>=&gt;</span><span> </span><span>n</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>setNumber</span><span><span>(</span><span>n</span><span> </span></span><span>=&gt;</span><span> </span><span>n</span><span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;+3&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在这里，<code>n =&gt; n + 1</code> 被称为 <strong>更新函数</strong>。当你将它传递给一个 state 设置函数时：</p><ol>
<li>React 会将此函数加入队列，以便在事件处理函数中的所有其他代码运行后进行处理。</li>
<li>在下一次渲染期间，React 会遍历队列并给你更新之后的最终 state。</li>
</ol><p>下面是 React 在执行事件处理函数时处理这几行代码的过程：</p><ol>
<li><code>setNumber(n =&gt; n + 1)</code>：<code>n =&gt; n + 1</code> 是一个函数。React 将它加入队列。</li>
<li><code>setNumber(n =&gt; n + 1)</code>：<code>n =&gt; n + 1</code> 是一个函数。React 将它加入队列。</li>
<li><code>setNumber(n =&gt; n + 1)</code>：<code>n =&gt; n + 1</code> 是一个函数。React 将它加入队列。</li>
</ol><p>当你在下次渲染期间调用 <code>useState</code> 时，React 会遍历队列。之前的 <code>number</code> state 的值是 <code>0</code>，所以这就是 React 作为参数 <code>n</code> 传递给第一个更新函数的值。然后 React 会获取你上一个更新函数的返回值，并将其作为 <code>n</code> 传递给下一个更新函数，以此类推：</p>

<table><thead><tr><th>更新队列</th><th><code>n</code></th><th>返回值</th></tr></thead><tbody><tr><td><code>n =&gt; n + 1</code></td><td><code>0</code></td><td><code>0 + 1 = 1</code></td></tr><tr><td><code>n =&gt; n + 1</code></td><td><code>1</code></td><td><code>1 + 1 = 2</code></td></tr><tr><td><code>n =&gt; n + 1</code></td><td><code>2</code></td><td><code>2 + 1 = 3</code></td></tr></tbody></table><p>React 会保存 <code>3</code> 为最终结果并从 <code>useState</code> 中返回。</p></section><section><h3>如果你在替换 state 后更新 state 会发生什么<a href="#如果你在替换-state-后更新-state-会发生什么"><span>#</span></a></h3><p>这个事件处理函数会怎么样？你认为 <code>number</code> 在下一次渲染中的值是什么？</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setNumber</span><span><span>(</span><span>number</span><span> </span><span>+</span><span> </span></span><span>5</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>setNumber</span><span><span>(</span><span>n</span><span> </span></span><span>=&gt;</span><span><span> </span><span>n</span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>}</span><span>}</span><span>&gt;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这是事件处理函数告诉 React 要做的事情：</p><ol>
<li><code>setNumber(number + 5)</code>：<code>number</code> 为 <code>0</code>，所以 <code>setNumber(0 + 5)</code>。React 将 <em>“替换为 <code>5</code>”</em> 添加到其队列中。</li>
<li><code>setNumber(n =&gt; n + 1)</code>：<code>n =&gt; n + 1</code> 是一个更新函数。 React 将 <strong>该函数</strong> 添加到其队列中。</li>
</ol><p>在下一次渲染期间，React 会遍历 state 队列：</p>

<table><thead><tr><th>更新队列</th><th><code>n</code></th><th>返回值</th></tr></thead><tbody><tr><td>“替换为 <code>5</code>”</td><td><code>0</code>（未使用）</td><td><code>5</code></td></tr><tr><td><code>n =&gt; n + 1</code></td><td><code>5</code></td><td><code>5 + 1 = 6</code></td></tr></tbody></table><p>React 会保存 <code>6</code> 为最终结果并从 <code>useState</code> 中返回。</p><p>你可能已经注意到，<code>setState(x)</code> 实际上会像 <code>setState(n =&gt; x)</code> 一样运行，只是没有使用 <code>n</code></p></section><section><h3>如果你在更新 state 后替换 state 会发生什么<a href="#如果你在更新-state-后替换-state-会发生什么"><span>#</span></a></h3><p><code>number</code> 在下一次渲染中的值是什么？</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setNumber</span><span><span>(</span><span>number</span><span> </span><span>+</span><span> </span></span><span>5</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>setNumber</span><span><span>(</span><span>n</span><span> </span></span><span>=&gt;</span><span><span> </span><span>n</span><span> </span><span>+</span><span> </span></span><span>1</span><span>);</span></div></div><div><div><div>4</div></div><div><span>  </span><span>setNumber</span><span>(</span><span>42</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>}</span><span>}</span><span>&gt;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>以下是 React 在执行事件处理函数时处理这几行代码的过程：</p><ol>
<li><code>setNumber(number + 5)</code>：<code>number</code> 为 <code>0</code>，所以 <code>setNumber(0 + 5)</code>。React 将 <em>“替换为 <code>5</code>”</em> 添加到其队列中。</li>
<li><code>setNumber(n =&gt; n + 1)</code>：<code>n =&gt; n + 1</code> 是一个更新函数。React 将该函数添加到其队列中。</li>
<li><code>setNumber(42)</code>：React 将 <em>“替换为 <code>42</code>”</em> 添加到其队列中。</li>
</ol><p>在下一次渲染期间，React 会遍历 state 队列：</p>

<table><thead><tr><th>更新队列</th><th><code>n</code></th><th>返回值</th></tr></thead><tbody><tr><td>“替换为 <code>5</code>”</td><td><code>0</code>（未使用）</td><td><code>5</code></td></tr><tr><td><code>n =&gt; n + 1</code></td><td><code>5</code></td><td><code>5 + 1 = 6</code></td></tr><tr><td>“替换为 <code>42</code>”</td><td><code>6</code>（未使用）</td><td><code>42</code></td></tr></tbody></table><p>然后 React 会保存 <code>42</code> 为最终结果并从 <code>useState</code> 中返回。</p><p>总而言之，以下是你可以考虑传递给 <code>setNumber</code> state 设置函数的内容：</p><ul>
<li><strong>一个更新函数</strong>（例如：<code>n =&gt; n + 1</code>）会被添加到队列中。</li>
<li><strong>任何其他的值</strong>（例如：数字 <code>5</code>）会导致“替换为 <code>5</code>”被添加到队列中，已经在队列中的内容会被忽略。</li>
</ul><p>事件处理函数执行完成后，React 将触发重新渲染。在重新渲染期间，React 将处理队列。更新函数会在渲染期间执行，因此 <strong>更新函数必须是 <a href="https://zh-hans.react.dev/learn/keeping-components-pure" target="_blank">纯函数</a></strong> 并且只 <strong>返回</strong> 结果。不要尝试从它们内部设置 state 或者执行其他副作用。</p></section></section>
<section><h2>更新 state 中的对象<a href="#更新-state-中的对象"><span>#</span></a></h2><p>state 中可以保存任意类型的 JavaScript 值，包括对象。但是，你不应该直接修改存放在 React state 中的对象。相反，当你想要更新一个对象时，你需要创建一个新的对象（或者将其拷贝一份），然后将 state 更新为此对象。</p><section><h3>将 state 视为只读的<a href="#将-state-视为只读的"><span>#</span></a></h3><p>你可以在 state 中存放任意类型的 JavaScript 值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>x</span><span>, </span><span>setX</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>到目前为止，你已经尝试过在 state 中存放数字、字符串和布尔值，这些类型的值在 JavaScript 中是不可变（immutable）的，这意味着它们不能被改变或是只读的。你可以通过替换它们的值以触发一次重新渲染。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setX</span><span>(</span><span>5</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>state <code>x</code> 从 <code>0</code> 变为 <code>5</code>，但是数字 <code>0</code> 本身并没有发生改变。在 JavaScript 中，无法对内置的原始值，如数字、字符串和布尔值，进行任何更改。</p><p>现在考虑 state 中存放对象的情况：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>position</span><span>, </span><span>setPosition</span><span>] </span><span>=</span><span> </span><span>useState</span><span>({ </span><span>x</span><span>:</span><span> </span><span>0</span><span>, </span><span>y</span><span>:</span><span> </span><span>0</span><span> });</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>从技术上来讲，可以改变对象自身的内容。<strong>当你这样做时，就制造了一个 mutation</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>position</span><span>.</span></span><span>x</span><span> </span><span>=</span><span> </span><span>5</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然而，虽然严格来说 React state 中存放的对象是可变的，但你应该像处理数字、布尔值、字符串一样将它们视为不可变的。因此你应该替换它们的值，而不是对它们进行修改。</p><p>换句话说，应该 <strong>把所有存放在 state 中的 JavaScript 对象都视为只读的</strong>。</p><p>在下面的例子中，我们用一个存放在 state 中的对象来表示指针当前的位置。当你在预览区触摸或移动光标时，红色的点本应移动。但是实际上红点仍停留在原处：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>MovingDot</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>position</span><span>, </span><span>setPosition</span><span>] </span><span>=</span><span> </span><span>useState</span><span>({</span></div></div><div><div><div>5</div></div><div><span>    </span><span>x</span><span>:</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>y</span><span>:</span><span> </span><span>0</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>8</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span></div></div><div><div><div>10</div></div><div><span>      </span><span>onPointerMove</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>position</span><span>.</span><span>x</span><span><span> </span><span>=</span><span> </span><span>e</span><span>.</span></span><span>clientX</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>position</span><span>.</span><span>y</span><span><span> </span><span>=</span><span> </span><span>e</span><span>.</span></span><span>clientY</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span><span>}</span></div></div><div><div><div>14</div></div><div><span>      </span><span>style</span><span><span>=</span><span>{</span><span>{</span></span></div></div><div><div><div>15</div></div><div><span>        </span><span>position</span><span>:</span><span> </span><span>'relative'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>        </span><span>width</span><span>:</span><span> </span><span>'100vw'</span><span>,</span></div></div><div><div><div>17</div></div><div><span>        </span><span>height</span><span>:</span><span> </span><span>'100vh'</span><span>,</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>&lt;</span><span>div</span><span> </span><span>style</span><span><span>=</span><span>{</span><span>{</span></span></div></div><div><div><div>20</div></div><div><span>        </span><span>position</span><span>:</span><span> </span><span>'absolute'</span><span>,</span></div></div><div><div><div>21</div></div><div><span>        </span><span>backgroundColor</span><span>:</span><span> </span><span>'red'</span><span>,</span></div></div><div><div><div>22</div></div><div><span>        </span><span>borderRadius</span><span>:</span><span> </span><span>'50%'</span><span>,</span></div></div><div><div><div>23</div></div><div><span>        </span><span>transform</span><span>:</span><span> </span><span>`translate(</span><span>${</span><span>position</span><span>.</span><span>x</span><span>}</span><span>px, </span><span>${</span><span>position</span><span>.</span><span>y</span><span>}</span><span>px)`</span><span>,</span></div></div><div><div><div>24</div></div><div><span>        </span><span>left</span><span>:</span><span><span> </span><span>-</span></span><span>10</span><span>,</span></div></div><div><div><div>25</div></div><div><span>        </span><span>top</span><span>:</span><span><span> </span><span>-</span></span><span>10</span><span>,</span></div></div><div><div><div>26</div></div><div><span>        </span><span>width</span><span>:</span><span> </span><span>20</span><span>,</span></div></div><div><div><div>27</div></div><div><span>        </span><span>height</span><span>:</span><span> </span><span>20</span><span>,</span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span> /&gt;</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>31</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>问题出在下面这段代码中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>onPointerMove</span><span>=</span><span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>position</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>clientX</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>position</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>clientY</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这段代码直接修改了上一次渲染中分配给 <code>position</code> 的对象。但是因为并没有使用 state 的设置函数，React 并不知道对象已更改。所以 React 没有做出任何响应。这就像在吃完饭之后才尝试去改变要点的菜一样。虽然在一些情况下，直接修改 state 可能是有效的，但我们并不推荐这么做。你应该把在渲染过程中可以访问到的 state 视为只读的。</p><p>在这种情况下，为了真正地触发一次重新渲染，<strong>你需要创建一个新对象并把它传递给 state 的设置函数</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>onPointerMove</span><span>=</span><span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setPosition</span><span>({</span></div></div><div><div><div>3</div></div><div><span>    </span><span>x</span><span>:</span><span><span> </span><span>e</span><span>.</span></span><span>clientX</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>y</span><span>:</span><span><span> </span><span>e</span><span>.</span></span><span>clientY</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>6</div></div><div><span>}}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>通过使用 <code>setPosition</code>，你在告诉 React：</p><ul>
<li>使用这个新的对象替换 <code>position</code> 的值</li>
<li>然后再次渲染这个组件</li>
</ul><blockquote><section><h4>使用展开语法复制对象<a href="#使用展开语法复制对象"><span>#</span></a></h4><p>如果只想改变对象的其中一个值，可以使用 <code>...</code> 复制整个对象，只更改需要变更的属性。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>onPointerMove</span><span>=</span><span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>setPosition</span><span>({</span></div></div><div><div><div>3</div></div><div><span>...</span><span><span>position</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span> </span><span>y</span><span>:</span><span><span> </span><span>e</span><span>.</span></span><span>clientY</span></div></div><div><div><div>5</div></div><div><span>});</span></div></div><div><div><div>6</div></div><div><span>}}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>只能在竖直方向移动</p></section></blockquote></section><section><h3>更新一个嵌套对象<a href="#更新一个嵌套对象"><span>#</span></a></h3><p>考虑下面这种结构的嵌套对象：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> [</span><span>person</span><span>, </span><span>setPerson</span><span>] </span><span>=</span><span> </span><span>useState</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>name</span><span>:</span><span> </span><span>'Niki de Saint Phalle'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>artwork</span><span>:</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>title</span><span>:</span><span> </span><span>'Blue Nana'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>city</span><span>:</span><span> </span><span>'Hamburg'</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>image</span><span>:</span><span> </span><span>'https://i.imgur.com/Sd1AgUOm.jpg'</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你想要更新 <code>person.artwork.city</code> 的值，用 mutation 来实现的方法非常容易理解：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>person</span><span>.</span></span><span>artwork</span><span>.</span><span>city</span><span> </span><span>=</span><span> </span><span>'New Delhi'</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是在 React 中，你需要将 state 视为不可变的！为了修改 <code>city</code> 的值，你首先需要创建一个新的 <code>artwork</code> 对象（其中预先填充了上一个 <code>artwork</code> 对象中的数据），然后创建一个新的 <code>person</code> 对象，并使得其中的 <code>artwork</code> 属性指向新创建的 <code>artwork</code> 对象：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>nextArtwork</span><span> </span><span>=</span><span> { </span><span>...</span><span><span>person</span><span>.</span></span><span>artwork</span><span>, </span><span>city</span><span>:</span><span> </span><span>'New Delhi'</span><span> };</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>nextPerson</span><span> </span><span>=</span><span> { </span><span>...</span><span><span>person</span><span>, </span></span><span>artwork</span><span>:</span><span><span> </span><span>nextArtwork</span><span> };</span></span></div></div><div><div><div>3</div></div><div><span>setPerson</span><span><span>(</span><span>nextPerson</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者，写成一个函数调用：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setPerson</span><span>({</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>...</span><span><span>person</span><span>, </span></span><span>// 复制其它字段的数据</span></div></div><div><div><div>3</div></div><div><span>  </span><span>artwork</span><span>:</span><span> { </span><span>// 替换 artwork 字段</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>...</span><span><span>person</span><span>.</span></span><span>artwork</span><span>, </span><span>// 复制之前 person.artwork 中的数据</span></div></div><div><div><div>5</div></div><div><span>    </span><span>city</span><span>:</span><span> </span><span>'New Delhi'</span><span> </span><span>// 但是将 city 的值替换为 New Delhi！</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Immer<a href="#immer"><span>#</span></a></h3><p>如果你的 state 有多层的嵌套，你或许应该考虑 <a href="https://zh-hans.react.dev/learn/choosing-the-state-structure#avoid-deeply-nested-state" target="_blank">将其扁平化</a>。但是，如果你不想改变 state 的数据结构，你可能更喜欢用一种更便捷的方式来实现嵌套展开的效果。<a href="https://github.com/immerjs/use-immer" target="_blank">Immer</a> 是一个非常流行的库，它可以让你使用简便但可以直接修改的语法编写代码，并会帮你处理好复制的过程。通过使用 Immer，你写出的代码看起来就像是你“打破了规则”而直接修改了对象：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>updatePerson</span><span><span>(</span><span>draft</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>draft</span><span>.</span><span>artwork</span><span>.</span><span>city</span><span> </span><span>=</span><span> </span><span>'Lagos'</span><span>;</span></div></div><div><div><div>3</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>尝试使用 Immer:</p><ol>
<li>运行 <code>npm install use-immer</code> 添加 Immer 依赖</li>
<li>用 <code>import { useImmer } from 'use-immer'</code> 替换掉 <code>import { useState } from 'react'</code></li>
</ol><blockquote><section><h4>为什么在 React 中不推荐直接修改 state？<a href="#为什么在-react-中不推荐直接修改-state"><span>#</span></a></h4><ul>
<li><strong>调试</strong>：如果你使用 <code>console.log</code> 并且不直接修改 state，你之前日志中的 state 的值就不会被新的 state 变化所影响。这样你就可以清楚地看到两次渲染之间 state 的值发生了什么变化</li>
<li><strong>优化</strong>：React 常见的 <a href="https://zh-hans.react.dev/reference/react/memo" target="_blank">优化策略</a> 依赖于如果之前的 props 或者 state 的值和下一次相同就跳过渲染。如果你从未直接修改 state ，那么你就可以很快看到 state 是否发生了变化。如果 <code>prevObj === obj</code>，那么你就可以肯定这个对象内部并没有发生改变。</li>
<li><strong>新功能</strong>：我们正在构建的 React 的新功能依赖于 state 被 <a href="https://zh-hans.react.dev/learn/state-as-a-snapshot" target="_blank">像快照一样看待</a> 的理念。如果你直接修改 state 的历史版本，可能会影响你使用这些新功能。</li>
<li><strong>需求变更</strong>：有些应用功能在不出现任何修改的情况下会更容易实现，比如实现撤销/恢复、展示修改历史，或是允许用户把表单重置成某个之前的值。这是因为你可以把 state 之前的拷贝保存到内存中，并适时对其进行再次使用。如果一开始就用了直接修改 state 的方式，那么后面要实现这样的功能就会变得非常困难。</li>
<li><strong>更简单的实现</strong>：React 并不依赖于 mutation ，所以你不需要对对象进行任何特殊操作。它不需要像很多“响应式”的解决方案一样去劫持对象的属性、总是用代理把对象包裹起来，或者在初始化时做其他工作。这也是 React 允许你把任何对象存放在 state 中——不管对象有多大——而不会造成有任何额外的性能或正确性问题的原因。</li>
</ul></section></blockquote></section></section>
<section><h2>更新 state 中的数组<a href="#更新-state-中的数组"><span>#</span></a></h2><p>数组是另外一种可以存储在 state 中的 JavaScript 对象，它虽然是可变的，但是却应该被视为不可变。同对象一样，当你想要更新存储于 state 中的数组时，你需要创建一个新的数组（或者创建一份已有数组的拷贝值），并使用新数组设置 state。</p><section><h3>在没有 mutation 的前提下更新数组<a href="#在没有-mutation-的前提下更新数组"><span>#</span></a></h3><p>在 JavaScript 中，数组只是另一种对象。<a href="https://zh-hans.react.dev/learn/updating-objects-in-state" target="_blank">同对象一样</a>，<strong>你需要将 React state 中的数组视为只读的</strong>。这意味着你不应该使用类似于 <code>arr[0] = 'bird'</code> 这样的方式来重新分配数组中的元素，也不应该使用会直接修改原始数组的方法，例如 <code>push()</code> 和 <code>pop()</code>。</p><p>相反，每次要更新一个数组时，你需要把一个<strong>新</strong>的数组传入 state 的 setting 方法中。为此，你可以通过使用像 <code>filter()</code> 和 <code>map()</code> 这样不会直接修改原始值的方法，从原始数组生成一个新的数组。然后你就可以将 state 设置为这个新生成的数组。</p><p>下面是常见数组操作的参考表。当你操作 React state 中的数组时，你需要避免使用左列的方法，而首选右列的方法：</p>

<table><thead><tr><th></th><th>避免使用 (会改变原始数组)</th><th>推荐使用 (会返回一个新数组）</th></tr></thead><tbody><tr><td>添加元素</td><td><code>push</code>，<code>unshift</code></td><td><code>concat</code>，<code>[...arr]</code> 展开语法</td></tr><tr><td>删除元素</td><td><code>pop</code>，<code>shift</code>，<code>splice</code></td><td><code>filter</code>，<code>slice</code></td></tr><tr><td>替换元素</td><td><code>splice</code>，<code>arr[i] = ...</code> 赋值</td><td><code>map</code></td></tr><tr><td>排序</td><td><code>reverse</code>，<code>sort</code></td><td>先将数组复制一份</td></tr></tbody></table><p>或者，你可以<a href="https://zh-hans.react.dev/learn/updating-arrays-in-state#write-concise-update-logic-with-immer" target="_blank">使用 Immer</a> ，这样你便可以使用表格中的所有方法了。</p></section><section><h3>向数组中添加元素<a href="#向数组中添加元素"><span>#</span></a></h3><p><code>push()</code> 会直接修改原始数组，而你不希望这样：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>nextId</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>List</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> [</span><span>name</span><span>, </span><span>setName</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>''</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> [</span><span>artists</span><span>, </span><span>setArtists</span><span>] </span><span>=</span><span> </span><span>useState</span><span>([]);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;振奋人心的雕塑家们：&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>&lt;</span><span>input</span></div></div><div><div><div>13</div></div><div><span>        </span><span>value</span><span><span>=</span><span>{</span></span><span>name</span><span>}</span></div></div><div><div><div>14</div></div><div><span>        </span><span>onChange</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> </span><span>setName</span><span><span>(</span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>value</span><span><span>)</span><span>}</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>/&gt;</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>artists</span><span>.</span><span>push</span><span>({</span></div></div><div><div><div>18</div></div><div><span>          </span><span>id</span><span>:</span><span> </span><span>nextId</span><span><span>++</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>          </span><span>name</span><span>:</span><span> </span><span>name</span><span>,</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>}</span><span>}</span><span>&gt;添加&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span>        </span><span>{</span><span><span>artists</span><span>.</span></span><span>map</span><span><span>(</span><span>artist</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>24</div></div><div><span><span>          </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>artist</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span><span>{</span><span>artist</span><span>.</span></span><span>name</span><span><span>}</span><span>&lt;/</span></span><span>li</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>))</span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>相反，你应该创建一个 <strong>新</strong> 数组，其包含了原始数组的所有元素 <strong>以及</strong> 一个在末尾的新元素。这可以通过很多种方法实现，最简单的一种就是使用 <code>...</code> <a href="https://zh-hans.react.dev/a-javascript-refresher#array-spread" target="_blank">数组展开</a> 语法：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setArtists</span><span>( </span><span>// 替换 state</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>[ </span><span>// 是通过传入一个新数组实现的</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>...</span><span><span>artists</span><span>, </span></span><span>// 新数组包含原数组的所有元素</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>{ </span><span>id</span><span>:</span><span><span> </span><span>nextId</span></span><span>++</span><span>, </span><span>name</span><span>:</span><span><span> </span><span>name</span><span> } </span></span><span>// 并在末尾添加了一个新的元素</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>6</div></div><div><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>数组展开运算符还允许你把新添加的元素放在原始的 <code>...artists</code> 之前：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setArtists</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span><span> </span><span>nextId</span></span><span>++</span><span>, </span><span>name</span><span>:</span><span><span> </span><span>name</span><span> },</span></span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>...</span><span><span>artists</span><span> </span></span><span>// 将原数组中的元素放在末尾</span></div></div><div><div><div>4</div></div><div><span>]);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样一来，展开操作就可以完成 <code>push()</code> 和 <code>unshift()</code> 的工作，将新元素添加到数组的末尾和开头。</p></section><section><h3>从数组中删除元素<a href="#从数组中删除元素"><span>#</span></a></h3><p>从数组中删除一个元素最简单的方法就是将它<strong>过滤出去</strong>。换句话说，你需要生成一个不包含该元素的新数组。这可以通过 <code>filter</code> 方法实现，例如：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>initialArtists</span><span> </span></span><span>=</span><span> [</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>0</span><span>, </span><span>name</span><span>:</span><span> </span><span>'Marta Colvin Andrade'</span><span> },</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>1</span><span>, </span><span>name</span><span>:</span><span> </span><span>'Lamidi Olonade Fakeye'</span><span> },</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>2</span><span>, </span><span>name</span><span>:</span><span> </span><span>'Louise Nevelson'</span><span> },</span></div></div><div><div><div>7</div></div><div><span>]</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>List</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> [</span><span>artists</span><span>, </span><span>setArtists</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialArtists</span><span>)</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;振奋人心的雕塑家们：&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span>        </span><span>{</span><span><span>artists</span><span>.</span></span><span>map</span><span><span>(</span><span>artist</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>artist</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span></span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>{</span><span>artist</span><span>.</span><span>name</span><span>}{</span><span>' '</span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>&lt;</span><span>button</span></div></div><div><div><div>20</div></div><div><span>              </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>21</div></div><div><span>                </span><span>setArtists</span><span><span>(</span><span>artists</span><span>.</span></span><span>filter</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span><span> </span><span>a</span><span>.</span></span><span>id</span><span><span> </span><span>!==</span><span> </span><span>artist</span><span>.</span></span><span>id</span><span>))</span></div></div><div><div><div>22</div></div><div><span><span>              </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span><span>              </span></span><span>删除</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>))</span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>      </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>artists.filter(s =&gt; s.id !== artist.id)</code> 表示“创建一个新的数组，该数组由那些 ID 与 <code>artists.id</code> 不同的 <code>artists</code> 组成”。换句话说，每个 artist 的“删除”按钮会把 <em>那一个</em> artist 从原始数组中过滤掉，并使用过滤后的数组再次进行渲染。注意，<code>filter</code> 并不会改变原始数组。</p></section><section><h3>转换数组<a href="#转换数组"><span>#</span></a></h3><p>如果你想改变数组中的某些或全部元素，你可以用 <code>map()</code> 创建一个<strong>新</strong>数组。你传入 <code>map</code> 的函数决定了要根据每个元素的值或索引（或二者都要）对元素做何处理。</p><p>在下面的例子中，一个数组记录了两个圆形和一个正方形的坐标。当你点击按钮时，仅有两个圆形会向下移动 100 像素。这是通过使用 <code>map()</code> 生成一个新数组实现的。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>initialShapes</span><span> </span></span><span>=</span><span> [</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>0</span><span>, </span><span>type</span><span>:</span><span> </span><span>'circle'</span><span>, </span><span>x</span><span>:</span><span> </span><span>50</span><span>, </span><span>y</span><span>:</span><span> </span><span>100</span><span> },</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>1</span><span>, </span><span>type</span><span>:</span><span> </span><span>'square'</span><span>, </span><span>x</span><span>:</span><span> </span><span>150</span><span>, </span><span>y</span><span>:</span><span> </span><span>100</span><span> },</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>2</span><span>, </span><span>type</span><span>:</span><span> </span><span>'circle'</span><span>, </span><span>x</span><span>:</span><span> </span><span>250</span><span>, </span><span>y</span><span>:</span><span> </span><span>100</span><span> },</span></div></div><div><div><div>7</div></div><div><span>]</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>ShapeEditor</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> [</span><span>shapes</span><span>, </span><span>setShapes</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialShapes</span><span>)</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>  </span><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>nextShapes</span><span> </span><span>=</span><span><span> </span><span>shapes</span><span>.</span></span><span>map</span><span><span>(</span><span>shape</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span>      </span><span>if</span><span><span> (</span><span>shape</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'square'</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>// 不作改变</span></div></div><div><div><div>16</div></div><div><span>        </span><span>return</span><span> </span><span>shape</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>// 返回一个新的圆形，位置在下方 50px 处</span></div></div><div><div><div>19</div></div><div><span>        </span><span>return</span><span> {</span></div></div><div><div><div>20</div></div><div><span><span>          </span></span><span>...</span><span>shape</span><span>,</span></div></div><div><div><div>21</div></div><div><span>          </span><span>y</span><span>:</span><span><span> </span><span>shape</span><span>.</span></span><span>y</span><span> </span><span>+</span><span> </span><span>50</span><span>,</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>25</div></div><div><span>    </span><span>// 使用新的数组进行重渲染</span></div></div><div><div><div>26</div></div><div><span>    </span><span>setShapes</span><span>(</span><span>nextShapes</span><span>)</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span></span><span>handleClick</span><span><span>}</span><span>&gt;所有圆形向下移动！&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>32</div></div><div><span>      </span><span>{</span><span><span>shapes</span><span>.</span></span><span>map</span><span><span>(</span><span>shape</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span></div></div><div><div><div>34</div></div><div><span>          </span><span>key</span><span><span>=</span><span>{</span><span>shape</span><span>.</span></span><span>id</span><span>}</span></div></div><div><div><div>35</div></div><div><span>          </span><span>style</span><span><span>=</span><span>{</span><span>{</span></span></div></div><div><div><div>36</div></div><div><span>            </span><span>background</span><span>:</span><span> </span><span>'purple'</span><span>,</span></div></div><div><div><div>37</div></div><div><span>            </span><span>position</span><span>:</span><span> </span><span>'absolute'</span><span>,</span></div></div><div><div><div>38</div></div><div><span>            </span><span>left</span><span>:</span><span><span> </span><span>shape</span><span>.</span></span><span>x</span><span>,</span></div></div><div><div><div>39</div></div><div><span>            </span><span>top</span><span>:</span><span><span> </span><span>shape</span><span>.</span></span><span>y</span><span>,</span></div></div><div><div><div>40</div></div><div><span>            </span><span>borderRadius</span><span>:</span><span><span> </span><span>shape</span><span>.</span></span><span>type</span><span><span> </span><span>===</span><span> </span></span><span>'circle'</span><span><span> </span><span>?</span><span> </span></span><span>'50%'</span><span><span> </span><span>:</span><span> </span></span><span>''</span><span>,</span></div></div><div><div><div>41</div></div><div><span>            </span><span>width</span><span>:</span><span> </span><span>20</span><span>,</span></div></div><div><div><div>42</div></div><div><span>            </span><span>height</span><span>:</span><span> </span><span>20</span><span>,</span></div></div><div><div><div>43</div></div><div><span><span>          </span></span><span>}</span><span>}</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>/&gt;</span></div></div><div><div><div>45</div></div><div><span><span>      </span></span><span>))</span><span>}</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>47</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>替换数组中的元素<a href="#替换数组中的元素"><span>#</span></a></h3><p>想要替换数组中一个或多个元素是非常常见的。类似 <code>arr[0] = 'bird'</code> 这样的赋值语句会直接修改原始数组，所以在这种情况下，你也应该使用 <code>map</code>。</p><p>要替换一个元素，请使用 <code>map</code> 创建一个新数组。在你的 <code>map</code> 回调里，第二个参数是元素的索引。使用索引来判断最终是返回原始的元素（即回调的第一个参数）还是替换成其他值：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>initialCounters</span><span> </span></span><span>=</span><span> [</span><span>0</span><span>, </span><span>0</span><span>, </span><span>0</span><span>]</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>CounterList</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> [</span><span>counters</span><span>, </span><span>setCounters</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialCounters</span><span>)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>function</span><span> </span><span>handleIncrementClick</span><span><span>(</span><span>index</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>nextCounters</span><span> </span><span>=</span><span><span> </span><span>counters</span><span>.</span></span><span>map</span><span><span>((</span><span>c</span><span>, </span><span>i</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>      </span><span>if</span><span> (</span><span>i</span><span> </span><span>===</span><span> </span><span>index</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>// 递增被点击的计数器数值</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span> </span><span>c</span><span> </span><span>+</span><span> </span><span>1</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>// 其余部分不发生变化</span></div></div><div><div><div>15</div></div><div><span>        </span><span>return</span><span> </span><span>c</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>18</div></div><div><span>    </span><span>setCounters</span><span>(</span><span>nextCounters</span><span>)</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span>      </span><span>{</span><span><span>counters</span><span>.</span></span><span>map</span><span><span>((</span><span>counter</span><span>, </span><span>i</span><span>) </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span></span><span>i</span><span><span>}</span><span>&gt;</span></span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>{</span><span>counter</span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>          </span></span><span>&lt;</span><span>button</span></div></div><div><div><div>27</div></div><div><span>            </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>28</div></div><div><span>              </span><span>handleIncrementClick</span><span>(</span><span>i</span><span>)</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>30</div></div><div><span><span>            </span></span><span>+1</span></div></div><div><div><div>31</div></div><div><span><span>          </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>33</div></div><div><span><span>      </span></span><span>))</span><span>}</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>35</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>36</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>向数组中插入元素<a href="#向数组中插入元素"><span>#</span></a></h3><p>有时，你也许想向数组特定位置插入一个元素，这个位置既不在数组开头，也不在末尾。为此，你可以将数组展开运算符 <code>...</code> 和 <code>slice()</code> 方法一起使用。<code>slice()</code> 方法让你从数组中切出“一片”。为了将元素插入数组，你需要先展开原数组在插入点之前的切片，然后插入新元素，最后展开原数组中剩下的部分。</p><p>下面的例子中，插入按钮总是会将元素插入到数组中索引为 <code>1</code> 的位置。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>nextId</span><span> </span></span><span>=</span><span> </span><span>3</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>initialArtists</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>0</span><span>, </span><span>name</span><span>:</span><span> </span><span>'Marta Colvin Andrade'</span><span> },</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>1</span><span>, </span><span>name</span><span>:</span><span> </span><span>'Lamidi Olonade Fakeye'</span><span> },</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>2</span><span>, </span><span>name</span><span>:</span><span> </span><span>'Louise Nevelson'</span><span> },</span></div></div><div><div><div>8</div></div><div><span>]</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>List</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>  </span><span>const</span><span> [</span><span>name</span><span>, </span><span>setName</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>''</span><span>)</span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> [</span><span>artists</span><span>, </span><span>setArtists</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialArtists</span><span>)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>const</span><span> </span><span>insertAt</span><span> </span><span>=</span><span> </span><span>1</span><span> </span><span>// 可能是任何索引</span></div></div><div><div><div>16</div></div><div><span>    </span><span>const</span><span> </span><span>nextArtists</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>17</div></div><div><span>      </span><span>// 插入点之前的元素：</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>...</span><span><span>artists</span><span>.</span></span><span>slice</span><span>(</span><span>0</span><span>, </span><span>insertAt</span><span>),</span></div></div><div><div><div>19</div></div><div><span>      </span><span>// 新的元素：</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>nextId</span><span>++</span><span>, </span><span>name</span><span>:</span><span> </span><span>name</span><span> },</span></div></div><div><div><div>21</div></div><div><span>      </span><span>// 插入点之后的元素：</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>...</span><span><span>artists</span><span>.</span></span><span>slice</span><span>(</span><span>insertAt</span><span>),</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>24</div></div><div><span>    </span><span>setArtists</span><span>(</span><span>nextArtists</span><span>)</span></div></div><div><div><div>25</div></div><div><span>    </span><span>setName</span><span>(</span><span>''</span><span>)</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>30</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;振奋人心的雕塑家们：&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>&lt;</span><span>input</span><span> </span><span>value</span><span><span>=</span><span>{</span></span><span>name</span><span><span>}</span><span> </span></span><span>onChange</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> </span><span>setName</span><span><span>(</span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>value</span><span><span>)</span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>32</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span></span><span>handleClick</span><span><span>}</span><span>&gt;插入&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>33</div></div><div><span><span>      </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>34</div></div><div><span>        </span><span>{</span><span><span>artists</span><span>.</span></span><span>map</span><span><span>(</span><span>artist</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>35</div></div><div><span><span>          </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>artist</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span><span>{</span><span>artist</span><span>.</span></span><span>name</span><span><span>}</span><span>&lt;/</span></span><span>li</span><span>&gt;</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>))</span><span>}</span></div></div><div><div><div>37</div></div><div><span><span>      </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>其他改变数组的情况<a href="#其他改变数组的情况"><span>#</span></a></h3><p>总会有一些事，是你仅仅依靠展开运算符和 <code>map()</code> 或者 <code>filter()</code> 等不会直接修改原值的方法所无法做到的。例如，你可能想翻转数组，或是对数组排序。而 JavaScript 中的 <code>reverse()</code> 和 <code>sort()</code> 方法会改变原数组，所以你无法直接使用它们。</p><p><strong>可以先拷贝这个数组，再改变这个拷贝后的值。</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>initialList</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>0</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Big Bellies'</span><span> },</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>1</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Lunar Landscape'</span><span> },</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>2</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Terracotta Army'</span><span> },</span></div></div><div><div><div>7</div></div><div><span>]</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>List</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> [</span><span>list</span><span>, </span><span>setList</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialList</span><span>)</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>  </span><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>nextList</span><span> </span><span>=</span><span> [</span><span>...</span><span>list</span><span>]</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>nextList</span><span>.</span><span>reverse</span><span>()</span></div></div><div><div><div>15</div></div><div><span>    </span><span>setList</span><span>(</span><span>nextList</span><span>)</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span></span><span>handleClick</span><span><span>}</span><span>&gt;翻转&lt;/</span></span><span>button</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>22</div></div><div><span>        </span><span>{</span><span><span>list</span><span>.</span></span><span>map</span><span><span>(</span><span>artwork</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>23</div></div><div><span><span>          </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>artwork</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span><span>{</span><span>artwork</span><span>.</span></span><span>title</span><span><span>}</span><span>&lt;/</span></span><span>li</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>))</span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>      </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在这段代码中，你先使用 <code>[...list]</code> 展开运算符创建了一份数组的拷贝值。当你有了这个拷贝值后，你就可以使用像 <code>nextList.reverse()</code> 或 <code>nextList.sort()</code> 这样直接修改原数组的方法。你甚至可以通过 <code>nextList[0] = "something"</code> 这样的方式对数组中的特定元素进行赋值。</p><p>然而，<strong>即使你拷贝了数组，你还是不能直接修改其内部的元素</strong>。这是因为数组的拷贝是浅拷贝——新的数组中依然保留了与原始数组相同的元素。因此，如果你修改了拷贝数组内部的某个对象，其实你正在直接修改当前的 state。举个例子，像下面的代码就会带来问题。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>nextList</span><span> </span><span>=</span><span> [</span><span>...</span><span><span>list</span><span>];</span></span></div></div><div><div><div>2</div></div><div><span><span>nextList</span><span>[</span></span><span>0</span><span>].</span><span>seen</span><span> </span><span>=</span><span> </span><span>true</span><span>; </span><span>// 问题：直接修改了 list[0] 的值</span></div></div><div><div><div>3</div></div><div><span>setList</span><span><span>(</span><span>nextList</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>虽然 <code>nextList</code> 和 <code>list</code> 是两个不同的数组，<strong><code>nextList[0]</code> 和 <code>list[0]</code> 却指向了同一个对象</strong>。因此，通过改变 <code>nextList[0].seen</code>，<code>list[0].seen</code> 的值也被改变了。这是一种 state 的 mutation 操作，应该避免这么做！你可以用类似于更新嵌套的 JavaScript 对象的方式解决这个问题——拷贝想要修改的特定元素，而不是直接修改它。下面是具体的操作。</p></section></section>
<section><h2>更新数组内部的对象<a href="#更新数组内部的对象"><span>#</span></a></h2><p>对象并不是 <em>真的</em> 位于数组“内部”。可能他们在代码中看起来像是在数组“内部”，但其实数组中的每个对象都是这个数组“指向”的一个存储于其它位置的值。这就是当你在处理类似 <code>list[0]</code> 这样的嵌套字段时需要格外小心的原因。其他人的艺术品清单可能指向了数组的同一个元素！</p><p><strong>当你更新一个嵌套的 state 时，你需要从想要更新的地方创建拷贝值，一直这样，直到顶层。</strong> 让我们看一下这该怎么做。</p><p>在下面的例子中，两个不同的艺术品清单有着相同的初始 state。他们本应该互不影响，但是因为一次 mutation，他们的 state 被意外地共享了，勾选一个清单中的事项会影响另外一个清单：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>nextId</span><span> </span></span><span>=</span><span> </span><span>3</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>initialList</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>0</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Big Bellies'</span><span>, </span><span>seen</span><span>:</span><span> </span><span>false</span><span> },</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>1</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Lunar Landscape'</span><span>, </span><span>seen</span><span>:</span><span> </span><span>false</span><span> },</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>2</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Terracotta Army'</span><span>, </span><span>seen</span><span>:</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>8</div></div><div><span>]</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>BucketList</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>  </span><span>const</span><span> [</span><span>myList</span><span>, </span><span>setMyList</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialList</span><span>)</span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> [</span><span>yourList</span><span>, </span><span>setYourList</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>initialList</span><span>)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>function</span><span> </span><span>handleToggleMyList</span><span><span>(</span><span>artworkId</span><span>, </span><span>nextSeen</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>const</span><span> </span><span>myNextList</span><span> </span><span>=</span><span><span> </span><span>myList</span><span>.</span></span><span>map</span><span><span>(</span><span>artwork</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span>      </span><span>if</span><span><span> (</span><span>artwork</span><span>.</span></span><span>id</span><span> </span><span>===</span><span> </span><span>artworkId</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>return</span><span> { </span><span>...</span><span>artwork</span><span>, </span><span>seen</span><span>:</span><span> </span><span>nextSeen</span><span> }</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>      </span><span>return</span><span> </span><span>artwork</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>21</div></div><div><span>    </span><span>setMyList</span><span>(</span><span>myNextList</span><span>)</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>  </span><span>function</span><span> </span><span>handleToggleYourList</span><span><span>(</span><span>artworkId</span><span>, </span><span>nextSeen</span><span>) {</span></span></div></div><div><div><div>25</div></div><div><span>    </span><span>const</span><span> </span><span>yourNextList</span><span> </span><span>=</span><span> [</span><span>...</span><span>yourList</span><span>]</span></div></div><div><div><div>26</div></div><div><span>    </span><span>const</span><span> </span><span>artwork</span><span> </span><span>=</span><span><span> </span><span>yourNextList</span><span>.</span></span><span>find</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span><span> </span><span>a</span><span>.</span></span><span>id</span><span> </span><span>===</span><span> </span><span>artworkId</span><span>)</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>artwork</span><span>.</span><span>seen</span><span> </span><span>=</span><span> </span><span>nextSeen</span></div></div><div><div><div>28</div></div><div><span>    </span><span>setYourList</span><span>(</span><span>yourNextList</span><span>)</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>33</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;艺术愿望清单&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>34</div></div><div><span><span>      </span></span><span>&lt;</span><span>h2</span><span>&gt;我想看的艺术清单：&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>35</div></div><div><span><span>      </span></span><span>&lt;</span><span>ItemList</span><span> </span><span>artworks</span><span><span>=</span><span>{</span></span><span>myList</span><span><span>}</span><span> </span></span><span>onToggle</span><span><span>=</span><span>{</span></span><span>handleToggleMyList</span><span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>36</div></div><div><span><span>      </span></span><span>&lt;</span><span>h2</span><span>&gt;你想看的艺术清单：&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>37</div></div><div><span><span>      </span></span><span>&lt;</span><span>ItemList</span><span> </span><span>artworks</span><span><span>=</span><span>{</span></span><span>yourList</span><span><span>}</span><span> </span></span><span>onToggle</span><span><span>=</span><span>{</span></span><span>handleToggleYourList</span><span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>function</span><span> </span><span>ItemList</span><span><span>({ </span><span>artworks</span><span>, </span><span>onToggle</span><span> }) {</span></span></div></div><div><div><div>43</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>45</div></div><div><span>      </span><span>{</span><span><span>artworks</span><span>.</span></span><span>map</span><span><span>(</span><span>artwork</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>artwork</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span></span></div></div><div><div><div>47</div></div><div><span><span>          </span></span><span>&lt;</span><span>label</span><span>&gt;</span></div></div><div><div><div>48</div></div><div><span><span>            </span></span><span>&lt;</span><span>input</span></div></div><div><div><div>49</div></div><div><span>              </span><span>type</span><span>=</span><span>'checkbox'</span></div></div><div><div><div>50</div></div><div><span>              </span><span>checked</span><span><span>=</span><span>{</span><span>artwork</span><span>.</span></span><span>seen</span><span>}</span></div></div><div><div><div>51</div></div><div><span>              </span><span>onChange</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>52</div></div><div><span>                </span><span>onToggle</span><span><span>(</span><span>artwork</span><span>.</span></span><span>id</span><span><span>, </span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>checked</span><span>)</span></div></div><div><div><div>53</div></div><div><span><span>              </span></span><span>}</span><span>}</span></div></div><div><div><div>54</div></div><div><span><span>            </span></span><span>/&gt;</span></div></div><div><div><div>55</div></div><div><span><span>            </span></span><span>{</span><span>artwork</span><span>.</span><span>title</span><span>}</span></div></div><div><div><div>56</div></div><div><span><span>          </span></span><span>&lt;/</span><span>label</span><span>&gt;</span></div></div><div><div><div>57</div></div><div><span><span>        </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>58</div></div><div><span><span>      </span></span><span>))</span><span>}</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>60</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在 <code>handleToggleYourList</code> 中，因为直接修改了已经有的对象，而其他对象又引用了这个对象，导致修改这个已有的对象时，会导致其他对象也发生变化。显然这是错误的。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>handleToggleYourList</span><span><span>(</span><span>artworkId</span><span>, </span><span>nextSeen</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>yourNextList</span><span> </span><span>=</span><span> [</span><span>...</span><span><span>yourList</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>artwork</span><span> </span><span>=</span><span><span> </span><span>yourNextList</span><span>.</span></span><span>find</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span><span> </span><span>a</span><span>.</span></span><span>id</span><span> </span><span>===</span><span><span> </span><span>artworkId</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>artwork</span><span>.</span><span>seen</span><span> </span><span>=</span><span><span> </span><span>nextSeen</span><span> </span></span><span>// 问题：直接修改了已有的元素</span></div></div><div><div><div>5</div></div><div><span>  </span><span>setYourList</span><span><span>(</span><span>yourNextList</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>虽然 <code>yourNextList</code> 这个数组是新的，但是其<strong>内部的元素本身</strong>与原数组 <code>yourList</code> 是相同的。因此，修改 <code>artwork.seen</code>，其实是在修改<strong>原始的</strong> artwork 对象。而这个 artwork 对象也被 <code>myList</code> 使用，这样就带来了 bug。如果你避免直接修改 state，它们就会消失。</p><p>使用 <code>map</code> 在没有 mutation 的前提下将一个旧的元素替换成更新的版本。 <code>handleToggleMyList</code> 通过 <code>map</code> 重新组成了一个数组，与 <code>yourList</code> 中的 artwork 指向不同的地址，所以在 <code>handleToggleMyList</code> 中就是正常的。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>handleToggleMyList</span><span><span>(</span><span>artworkId</span><span>, </span><span>nextSeen</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>myNextList</span><span> </span><span>=</span><span><span> </span><span>myList</span><span>.</span></span><span>map</span><span><span>(</span><span>artwork</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span><span> (</span><span>artwork</span><span>.</span></span><span>id</span><span> </span><span>===</span><span><span> </span><span>artworkId</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>      </span><span>return</span><span> { </span><span>...</span><span><span>artwork</span><span>, </span></span><span>seen</span><span>:</span><span><span> </span><span>nextSeen</span><span> }</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span><span> </span><span>artwork</span></span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>})</span></div></div><div><div><div>8</div></div><div><span>  </span><span>setMyList</span><span><span>(</span><span>myNextList</span><span>)</span></span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>使用 Immer 编写简洁的更新逻辑<a href="#使用-immer-编写简洁的更新逻辑"><span>#</span></a></h3><p>在没有 mutation 的前提下更新嵌套数组可能会变得有点重复。<a href="https://zh-hans.react.dev/learn/updating-objects-in-state#write-concise-update-logic-with-immer" target="_blank">就像对对象一样</a>:</p><ul>
<li>通常情况下，你应该不需要更新处于非常深层级的 state 。如果你有此类需求，你或许需要<a href="https://zh-hans.react.dev/learn/choosing-the-state-structure#avoid-deeply-nested-state" target="_blank">调整一下数据的结构</a>，让数据变得扁平一些。</li>
<li>如果你不想改变 state 的数据结构，你也许会更喜欢使用 <a href="https://github.com/immerjs/use-immer" target="_blank">Immer</a> ，它让你可以继续使用方便的，但会直接修改原值的语法，并负责为你生成拷贝值。</li>
</ul><p>下面是用 Immer 来重写的艺术愿望清单的例子：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span> } </span><span>from</span><span> </span><span>'react'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>useImmer</span><span> } </span><span>from</span><span> </span><span>'use-immer'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>nextId</span><span> </span></span><span>=</span><span> </span><span>3</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>initialList</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>0</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Big Bellies'</span><span>, </span><span>seen</span><span>:</span><span> </span><span>false</span><span> },</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>1</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Lunar Landscape'</span><span>, </span><span>seen</span><span>:</span><span> </span><span>false</span><span> },</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>{ </span><span>id</span><span>:</span><span> </span><span>2</span><span>, </span><span>title</span><span>:</span><span> </span><span>'Terracotta Army'</span><span>, </span><span>seen</span><span>:</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>9</div></div><div><span>]</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>BucketList</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> [</span><span>myList</span><span>, </span><span>updateMyList</span><span>] </span><span>=</span><span> </span><span>useImmer</span><span>(</span><span>initialList</span><span>)</span></div></div><div><div><div>13</div></div><div><span>  </span><span>const</span><span> [</span><span>yourList</span><span>, </span><span>updateYourList</span><span>] </span><span>=</span><span> </span><span>useImmer</span><span>(</span><span>initialList</span><span>)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>  </span><span>function</span><span> </span><span>handleToggleMyList</span><span><span>(</span><span>id</span><span>, </span><span>nextSeen</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>updateMyList</span><span><span>(</span><span>draft</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span>      </span><span>const</span><span> </span><span>artwork</span><span> </span><span>=</span><span><span> </span><span>draft</span><span>.</span></span><span>find</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span><span> </span><span>a</span><span>.</span></span><span>id</span><span> </span><span>===</span><span> </span><span>id</span><span>)</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>artwork</span><span>.</span><span>seen</span><span> </span><span>=</span><span> </span><span>nextSeen</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>  </span><span>function</span><span> </span><span>handleToggleYourList</span><span><span>(</span><span>artworkId</span><span>, </span><span>nextSeen</span><span>) {</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>updateYourList</span><span><span>(</span><span>draft</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>24</div></div><div><span>      </span><span>const</span><span> </span><span>artwork</span><span> </span><span>=</span><span><span> </span><span>draft</span><span>.</span></span><span>find</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span><span> </span><span>a</span><span>.</span></span><span>id</span><span> </span><span>===</span><span> </span><span>artworkId</span><span>)</span></div></div><div><div><div>25</div></div><div><span><span>      </span></span><span>artwork</span><span>.</span><span>seen</span><span> </span><span>=</span><span> </span><span>nextSeen</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>&lt;&gt;</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;艺术愿望清单&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>32</div></div><div><span><span>      </span></span><span>&lt;</span><span>h2</span><span>&gt;我想看的艺术清单：&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>33</div></div><div><span><span>      </span></span><span>&lt;</span><span>ItemList</span><span> </span><span>artworks</span><span><span>=</span><span>{</span></span><span>myList</span><span><span>}</span><span> </span></span><span>onToggle</span><span><span>=</span><span>{</span></span><span>handleToggleMyList</span><span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>34</div></div><div><span><span>      </span></span><span>&lt;</span><span>h2</span><span>&gt;你想看的艺术清单：&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>35</div></div><div><span><span>      </span></span><span>&lt;</span><span>ItemList</span><span> </span><span>artworks</span><span><span>=</span><span>{</span></span><span>yourList</span><span><span>}</span><span> </span></span><span>onToggle</span><span><span>=</span><span>{</span></span><span>handleToggleYourList</span><span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>&lt;/&gt;</span></div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>function</span><span> </span><span>ItemList</span><span><span>({ </span><span>artworks</span><span>, </span><span>onToggle</span><span> }) {</span></span></div></div><div><div><div>41</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>43</div></div><div><span>      </span><span>{</span><span><span>artworks</span><span>.</span></span><span>map</span><span><span>(</span><span>artwork</span><span> </span></span><span>=&gt;</span><span> (</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>artwork</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span></span></div></div><div><div><div>45</div></div><div><span><span>          </span></span><span>&lt;</span><span>label</span><span>&gt;</span></div></div><div><div><div>46</div></div><div><span><span>            </span></span><span>&lt;</span><span>input</span></div></div><div><div><div>47</div></div><div><span>              </span><span>type</span><span>=</span><span>'checkbox'</span></div></div><div><div><div>48</div></div><div><span>              </span><span>checked</span><span><span>=</span><span>{</span><span>artwork</span><span>.</span></span><span>seen</span><span>}</span></div></div><div><div><div>49</div></div><div><span>              </span><span>onChange</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>50</div></div><div><span>                </span><span>onToggle</span><span><span>(</span><span>artwork</span><span>.</span></span><span>id</span><span><span>, </span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>checked</span><span>)</span></div></div><div><div><div>51</div></div><div><span><span>              </span></span><span>}</span><span>}</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>/&gt;</span></div></div><div><div><div>53</div></div><div><span><span>            </span></span><span>{</span><span>artwork</span><span>.</span><span>title</span><span>}</span></div></div><div><div><div>54</div></div><div><span><span>          </span></span><span>&lt;/</span><span>label</span><span>&gt;</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>56</div></div><div><span><span>      </span></span><span>))</span><span>}</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>58</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>59</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>请注意当使用 Immer 时，<strong>类似 <code>artwork.seen = nextSeen</code> 这种会产生 mutation 的语法不会再有任何问题了：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>updateMyTodos</span><span><span>(</span><span>draft</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>artwork</span><span> </span><span>=</span><span><span> </span><span>draft</span><span>.</span></span><span>find</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span><span> </span><span>a</span><span>.</span></span><span>id</span><span> </span><span>===</span><span><span> </span><span>artworkId</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>artwork</span><span>.</span><span>seen</span><span> </span><span>=</span><span><span> </span><span>nextSeen</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这是因为你并不是在直接修改原始的 state，而是在修改 Immer 提供的一个特殊的 <code>draft</code> 对象。同理，你也可以为 <code>draft</code> 的内容使用 <code>push()</code> 和 <code>pop()</code> 这些会直接修改原值的方法。在幕后，Immer 总是会根据你对 <code>draft</code> 的修改来从头开始构建下一个 state。这使得你的事件处理程序非常的简洁，同时也不会直接修改 state。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/react-shi-jian/</id>
      <title type="text">react 事件</title>
      <published>2026-01-11T20:20:31.000Z</published>
      <updated>2026-01-11T20:20:31.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/react-shi-jian/"/>
      <summary type="text">使用 React 可以在 JSX 中添加 事件处理函数 。其中事件处理函数为自定义函数，它将在响应交互（如点击、悬停、表单输入框获得焦点等）时触发。 如需添加一个事件处理函数，需要先定义一个函数，然后 将其作为 prop 传入 合适的 JSX 标签。例如，这里有一个没绑定任何事件的按钮： 1. 在…</summary>
      <content type="html"><![CDATA[<p>使用 React 可以在 JSX 中添加 <strong>事件处理函数</strong>。其中事件处理函数为自定义函数，它将在响应交互（如点击、悬停、表单输入框获得焦点等）时触发。</p>
<section><h2>添加事件处理函数<a href="#添加事件处理函数"><span>#</span></a></h2><p>如需添加一个事件处理函数，需要先定义一个函数，然后 <a href="https://zh-hans.react.dev/learn/passing-props-to-a-component" target="_blank">将其作为 prop 传入</a> 合适的 JSX 标签。例如，这里有一个没绑定任何事件的按钮：</p><ol>
<li>在 <code>Button</code> 组件 <strong>内部</strong> 声明一个名为 <code>handleClick</code> 的函数。</li>
<li>实现函数内部的逻辑（使用 <code>alert</code> 来显示消息）。</li>
<li>添加 <code>onClick={handleClick}</code> 到 <code>&lt;button&gt;</code> JSX 中。</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Button</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>alert</span><span>(</span><span>'你点击了我！'</span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span></span><span>handleClick</span><span><span>}</span><span>&gt;</span></span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>点我</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你可以定义 <code>handleClick</code> 函数然后 <a href="https://zh-hans.react.dev/learn/passing-props-to-a-component" target="_blank">将其作为 prop 传入</a> <code>&lt;button&gt;</code>。其中 <code>handleClick</code> 是一个 <strong>事件处理函数</strong> 。事件处理函数有如下特点:</p><ul>
<li>通常在你的组件 <strong>内部</strong> 定义。</li>
<li>名称以 <code>handle</code> 开头，后跟事件名称。</li>
</ul><blockquote><p>按照惯例，通常将事件处理程序命名为 <code>handle</code>，后接事件名。你会经常看到 <code>onClick={handleClick}</code>，<code>onMouseEnter={handleMouseEnter}</code> 等。</p></blockquote><p>或者，你也可以在 JSX 中定义一个内联的事件处理函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span></span><span>function</span><span> </span><span>handleClick</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>alert</span><span>(</span><span>'你点击了我！'</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>}</span><span>}</span><span>&gt;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者，直接使用更为简洁箭头函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>alert</span><span>(</span><span>'你点击了我！'</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>}</span><span>}</span><span>&gt;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>以上所有方式都是等效的。当函数体较短时，内联事件处理函数会很方便。</p><section><h3>在事件处理函数中读取 props<a href="#在事件处理函数中读取-props"><span>#</span></a></h3><p>由于事件处理函数声明于组件内部，因此它们可以直接访问组件的 props。示例中的按钮，当点击时会弹出带有 <code>message</code> prop 的 alert：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>AlertButton</span><span><span>({ </span><span>message</span><span>, </span><span>children</span><span> }) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span><span>(</span><span>message</span><span>)</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>{</span><span>children</span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Toolbar</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;</span><span>AlertButton</span><span> </span><span>message</span><span>=</span><span>"正在播放！"</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>播放电影</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>&lt;/</span><span>AlertButton</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>将事件处理函数作为 props 传递<a href="#将事件处理函数作为-props-传递"><span>#</span></a></h3><p>当你的组件支持多种交互时，你可以根据不同的应用程序命名事件处理函数 prop。例如，一个 <code>Toolbar</code> 组件接收 <code>onPlayMovie</code> 和 <code>onUploadImage</code> 两个事件处理函数：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>App</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>Toolbar</span></div></div><div><div><div>4</div></div><div><span>      </span><span>onPlayMovie</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在播放！'</span><span><span>)</span><span>}</span></span></div></div><div><div><div>5</div></div><div><span>      </span><span>onUploadImage</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在上传！'</span><span><span>)</span><span>}</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>/&gt;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>Toolbar</span><span><span>({ </span><span>onPlayMovie</span><span>, </span><span>onUploadImage</span><span> }) {</span></span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>&lt;</span><span>Button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>onPlayMovie</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>播放电影</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>&lt;/</span><span>Button</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>&lt;</span><span>Button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>onUploadImage</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>上传图片</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>&lt;/</span><span>Button</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>function</span><span> </span><span>Button</span><span><span>({ </span><span>onClick</span><span>, </span><span>children</span><span> }) {</span></span></div></div><div><div><div>24</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>onClick</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>{</span><span>children</span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>命名事件处理函数 prop<a href="#命名事件处理函数-prop"><span>#</span></a></h3><p>内置组件（<code>&lt;button&gt;</code> 和 <code>&lt;div&gt;</code>）仅支持 <a href="https://zh-hans.react.dev/reference/react-dom/components/common#common-props" target="_blank">浏览器事件名称</a>，例如 <code>onClick</code>。但是，当你构建自己的组件时，你可以按你个人喜好命名事件处理函数的 prop。</p><blockquote><p>按照惯例，事件处理函数 props 应该以 <code>on</code> 开头，后跟一个大写字母。</p></blockquote><p>例如，一个 <code>Toolbar</code> 组件接收 <code>onPlayMovie</code> 和 <code>onUploadImage</code> 两个事件处理函数：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>App</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>Toolbar</span></div></div><div><div><div>4</div></div><div><span>      </span><span>onPlayMovie</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在播放！'</span><span><span>)</span><span>}</span></span></div></div><div><div><div>5</div></div><div><span>      </span><span>onUploadImage</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在上传！'</span><span><span>)</span><span>}</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>/&gt;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>Toolbar</span><span><span>({ </span><span>onPlayMovie</span><span>, </span><span>onUploadImage</span><span> }) {</span></span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>&lt;</span><span>Button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>onPlayMovie</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>播放电影</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>&lt;/</span><span>Button</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>&lt;</span><span>Button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>onUploadImage</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>上传图片</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>&lt;/</span><span>Button</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>function</span><span> </span><span>Button</span><span><span>({ </span><span>onClick</span><span>, </span><span>children</span><span> }) {</span></span></div></div><div><div><div>24</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>onClick</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>{</span><span>children</span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>App</code> 组件并不需要知道 <code>Toolbar</code> 将会对 <code>onPlayMovie</code> 和 <code>onUploadImage</code> 做 <strong>什么</strong> 。上述示例是 <code>Toolbar</code> 的实现细节。其中，<code>Toolbar</code> 只是将它们作为 <code>onClick</code> 处理函数传递给了 <code>Button</code> 组件，事件由 <code>Button</code> 组件触发。</p></section></section>
<section><h2>事件传播<a href="#事件传播"><span>#</span></a></h2><p>事件处理函数还将捕获任何来自子组件的事件。通常，我们会说事件会沿着树向上“冒泡”或“传播”：它从事件发生的地方开始，然后沿着树向上传播。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Toolbar</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>className</span><span>=</span><span>"Toolbar"</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>alert</span><span>(</span><span>'你点击了 toolbar ！'</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在播放！'</span><span><span>)</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>播放电影</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在上传！'</span><span><span>)</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>上传图片</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你点击任一按钮，它自身的 <code>onClick</code> 将首先执行，然后父级 <code>&lt;div&gt;</code> 的 <code>onClick</code> 会接着执行。因此会出现两条消息。如果你点击 toolbar 本身，将只有父级 <code>&lt;div&gt;</code> 的 <code>onClick</code> 会执行。</p><blockquote><p>在 React 中所有事件都会传播，除了 <code>onScroll</code>，它仅适用于你附加到的 JSX 标签。</p></blockquote><section><h3>阻止传播<a href="#阻止传播"><span>#</span></a></h3><p>事件处理函数接收一个 <strong>事件对象</strong> 作为唯一的参数。按照惯例，它通常被称为 <code>e</code> ，代表 “event”（事件）。你可以使用此对象来读取有关事件的信息。</p><p>这个事件对象还允许你阻止传播。如果你想阻止一个事件到达父组件，你需要像下面 <code>Button</code> 组件那样调用 <code>e.stopPropagation()</code> ：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Button</span><span><span>({ </span><span>onClick</span><span>, </span><span>children</span><span> }) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>e</span><span>.</span><span>stopPropagation</span><span>(); </span><span>//</span></div></div><div><div><div>5</div></div><div><span>      </span><span>onClick</span><span>();</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>{</span><span>children</span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Toolbar</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>className</span><span>=</span><span>"Toolbar"</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span>      </span><span>alert</span><span>(</span><span>'你点击了 toolbar ！'</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>&lt;</span><span>Button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在播放！'</span><span><span>)</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>播放电影</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>&lt;/</span><span>Button</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>&lt;</span><span>Button</span><span> </span><span>onClick</span><span><span>=</span><span>{</span><span>() </span></span><span>=&gt;</span><span> </span><span>alert</span><span>(</span><span>'正在上传！'</span><span><span>)</span><span>}</span><span>&gt;</span></span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>上传图片</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>&lt;/</span><span>Button</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>当你点击按钮时：</p><ol>
<li>
<p>React 调用了传递给 <code>&lt;button&gt;</code> 的 <code>onClick</code> 处理函数。</p>
</li>
<li>
<p>定义在 <code>Button</code> 中的处理函数执行了如下操作：</p>
<ul>
<li>调用 <code>e.stopPropagation()</code>，阻止事件进一步冒泡。</li>
<li>调用 <code>onClick</code> 函数，它是从 <code>Toolbar</code> 组件传递过来的 prop。</li>
</ul>
</li>
<li>
<p>在 <code>Toolbar</code> 组件中定义的函数，显示按钮对应的 alert。</p>
</li>
<li>
<p>由于传播被阻止，父级 <code>&lt;div&gt;</code> 的 <code>onClick</code> 处理函数不会执行。</p>
</li>
</ol><p>由于调用了 <code>e.stopPropagation()</code>，点击按钮现在将只显示一个 alert（来自 <code>&lt;button&gt;</code>），而并非两个（分别来自 <code>&lt;button&gt;</code> 和父级 toolbar <code>&lt;div&gt;</code>）。点击按钮与点击周围的 toolbar 不同，因此阻止传播对这个 UI 是有意义的。</p></section><section><h3>阻止默认行为<a href="#阻止默认行为"><span>#</span></a></h3><p>某些浏览器事件具有与事件相关联的默认行为。例如，点击 <code>&lt;form&gt;</code> 表单内部的按钮会触发表单提交事件，默认情况下将重新加载整个页面，可以调用事件对象中的 <code>e.preventDefault()</code> 来阻止这种情况发生：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Signup</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>form</span><span> </span><span>onSubmit</span><span><span>=</span><span>{</span><span>e</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>e</span><span>.</span><span>preventDefault</span><span>();</span></div></div><div><div><div>5</div></div><div><span>      </span><span>alert</span><span>(</span><span>'提交表单！'</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span><span>}</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;</span><span>input</span><span> /&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;</span><span>button</span><span>&gt;发送&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/</span><span>form</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><ul>
<li><a href="https://developer.mozilla.org/docs/Web/API/Event/stopPropagation" target="_blank"><code>e.stopPropagation()</code></a> 阻止触发绑定在外层标签上的事件处理函数。</li>
<li><a href="https://developer.mozilla.org/docs/Web/API/Event/preventDefault" target="_blank"><code>e.preventDefault()</code></a> 阻止少数事件的默认浏览器行为。</li>
</ul></blockquote></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/react-ji-chu/</id>
      <title type="text">react 基础</title>
      <published>2026-01-11T20:18:45.000Z</published>
      <updated>2026-01-11T20:18:45.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/react-ji-chu/"/>
      <summary type="text">React 组件使用 props 来互相通信。每个父组件都可以提供 props 给它的子组件，从而将一些信息传递给它。可以通过它们传递任何 JavaScript 值，包括对象、数组和函数。 首先，将一些 props 传递给子组件 Avatar 。例如，传递两个 props： person （一个对象…</summary>
      <content type="html"><![CDATA[<section><h2>将 Props 传递给组件<a href="#将-props-传递给组件"><span>#</span></a></h2><p>React 组件使用 <em>props</em> 来互相通信。每个父组件都可以提供 props 给它的子组件，从而将一些信息传递给它。可以通过它们传递任何 JavaScript 值，包括对象、数组和函数。</p><p>首先，将一些 props 传递给子组件 <code>Avatar</code>。例如，传递两个 props：<code>person</code>（一个对象）和 <code>size</code>（一个数字）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Profile</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>Avatar</span></div></div><div><div><div>4</div></div><div><span>      </span><span>person</span><span><span>=</span><span>{</span><span>{ </span></span><span>name</span><span>:</span><span> </span><span>'Lin Lanying'</span><span>, </span><span>imageId</span><span>:</span><span> </span><span>'1bX5QH6'</span><span><span> }</span><span>}</span></span></div></div><div><div><div>5</div></div><div><span>      </span><span>size</span><span><span>=</span><span>{</span></span><span>100</span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>/&gt;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>React 组件函数接受一个参数，一个 <code>props</code> 对象，并解构为单独的属性：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Avatar</span><span><span>({ </span><span>person</span><span>, </span><span>size</span><span> </span></span><span>=</span><span> </span><span>100</span><span> }) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>src</span><span> </span><span>=</span><span> </span><span>`https://i.imgur.com/</span><span>${</span><span>person</span><span>.</span><span>imageId</span><span>}</span><span>.jpg`</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> &lt;</span><span>img</span><span> </span><span>className</span><span>=</span><span>'avatar'</span><span> </span><span>src</span><span><span>=</span><span>{</span><span>src</span><span>}</span><span> </span></span><span>alt</span><span>=</span><span>'Lin Lanying'</span><span> </span><span>width</span><span><span>=</span><span>{</span><span>size</span><span>}</span><span> </span></span><span>height</span><span><span>=</span><span>{</span><span>size</span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>有时候，传递 props 会变得非常重复：当父组件想将一些porps传递到原封不动传递到子组件时，因为这些组件不直接使用他们本身的任何 props，可以使用更简洁的“展开”语法：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Father</span><span><span>(</span><span>props</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>className</span><span>=</span><span>"card"</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>&lt;</span><span>Child</span><span><span> </span><span>{</span><span>...</span><span>props</span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>将 JSX 作为子组件传递<a href="#将-jsx-作为子组件传递"><span>#</span></a></h2><p>嵌套浏览器内置标签是很常见的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>img</span><span> /&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>有时你会希望以相同的方式嵌套自己的组件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>Card</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>Avatar</span><span> /&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;/</span><span>Card</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当你将内容嵌套在 JSX 标签中时，父组件将在名为 <code>children</code> 的 prop 中接收到该内容。例如，下面的 <code>Card</code> 组件将接收一个被设为 <code>&lt;Avatar /&gt;</code> 的 <code>children</code> prop 并将其包裹在 div 中渲染：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Avatar</span><span><span>({ </span><span>person</span><span>, </span><span>size</span><span> </span></span><span>=</span><span> </span><span>100</span><span> }) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>src</span><span> </span><span>=</span><span> </span><span>`https://i.imgur.com/</span><span>${</span><span>person</span><span>.</span><span>imageId</span><span>}</span><span>.jpg`</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> &lt;</span><span>img</span><span> </span><span>className</span><span>=</span><span>'avatar'</span><span> </span><span>src</span><span><span>=</span><span>{</span><span>src</span><span>}</span><span> </span></span><span>alt</span><span>=</span><span>'Lin Lanying'</span><span> </span><span>width</span><span><span>=</span><span>{</span><span>size</span><span>}</span><span> </span></span><span>height</span><span><span>=</span><span>{</span><span>size</span><span>}</span><span> /&gt;</span></span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>function</span><span> </span><span>Card</span><span><span>({ </span><span>children</span><span>, </span><span>name</span><span> }) {</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>className</span><span>=</span><span>'card'</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;</span><span>h3</span><span><span>&gt;</span><span>{</span><span>name</span><span>}</span><span>&lt;/</span></span><span>h3</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>{</span><span>children</span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>Profile</span><span>() {</span></div></div><div><div><div>16</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>&lt;</span><span>Card</span><span> </span><span>name</span><span>=</span><span>'card'</span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>&lt;</span><span>Avatar</span></div></div><div><div><div>19</div></div><div><span>        </span><span>size</span><span><span>=</span><span>{</span></span><span>100</span><span>}</span></div></div><div><div><div>20</div></div><div><span>        </span><span>person</span><span><span>=</span><span>{</span><span>{</span></span></div></div><div><div><div>21</div></div><div><span>          </span><span>name</span><span>:</span><span> </span><span>'Katsuko Saruhashi'</span><span>,</span></div></div><div><div><div>22</div></div><div><span>          </span><span>imageId</span><span>:</span><span> </span><span>'YfeOqp2'</span><span>,</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>}</span><span>}</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>/&gt;</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>&lt;/</span><span>Card</span><span>&gt;</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>可以将带有 <code>children</code> prop 的组件看作有一个“洞”，可以由其父组件使用任意 JSX 来“填充”。在 React 中，<code>children</code> 属性就是 Vue 的默认插槽 (<code>&lt;slot /&gt;</code>) 的对应物。React 会自动将这些内容作为一个名为 <code>children</code> 的 prop 传递给该组件。</p><p>若组件包含两个元素以上，<code>children</code> 会作为一个数组接受所有的元素。</p><blockquote><ul>
<li>要传递 props，请将它们添加到 JSX，就像使用 HTML 属性一样。</li>
<li>要读取 props，请使用 <code>function Avatar({ person, size })</code> 解构语法。</li>
<li>你可以指定一个默认值，如 <code>size = 100</code>，用于缺少值或值为 <code>undefined</code> 的 props 。</li>
<li>你可以使用 <code>&lt;Avatar {...props} /&gt;</code> JSX 展开语法转发所有 props，但不要过度使用它！</li>
<li>像 <code>&lt;Card&gt;&lt;Avatar /&gt;&lt;/Card&gt;</code> 这样的嵌套 JSX，将被视为 <code>Card</code> 组件的 <code>children</code> prop。</li>
<li>Props 是只读的时间快照：每次渲染都会收到新版本的 props。</li>
<li>你不能改变 props。当你需要交互性时，你可以设置 state。</li>
</ul></blockquote></section>
<section><h2>条件渲染<a href="#条件渲染"><span>#</span></a></h2><section><h3>条件返回 JSX<a href="#条件返回-jsx"><span>#</span></a></h3><p>假设有一个 <code>PackingList</code> 组件，里面渲染多个 <code>Item</code> 组件，每个物品可标记为打包与否：</p><p>可以用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else" target="_blank">if/else 语句</a> 去判断：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Item</span><span><span>({ </span><span>name</span><span>, </span><span>isPacked</span><span> }) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>if</span><span><span> (</span><span>isPacked</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span> &lt;</span><span>li</span><span> </span><span>className</span><span>=</span><span>"item"</span><span><span>&gt;</span><span>{</span><span>name</span><span>}</span><span> ✅&lt;/</span></span><span>li</span><span>&gt;;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>  </span><span>return</span><span> &lt;</span><span>li</span><span> </span><span>className</span><span>=</span><span>"item"</span><span><span>&gt;</span><span>{</span><span>name</span><span>}</span><span>&lt;/</span></span><span>li</span><span>&gt;;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>PackingList</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>  </span><span>return</span><span> (</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;</span><span>section</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;Sally Ride 的行李清单&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>&lt;</span><span>Item</span></div></div><div><div><div>14</div></div><div><span>          </span><span>isPacked</span><span><span>=</span><span>{</span></span><span>true</span><span>}</span></div></div><div><div><div>15</div></div><div><span>          </span><span>name</span><span>=</span><span>"宇航服"</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>/&gt;</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>&lt;</span><span>Item</span></div></div><div><div><div>18</div></div><div><span>          </span><span>isPacked</span><span><span>=</span><span>{</span></span><span>true</span><span>}</span></div></div><div><div><div>19</div></div><div><span>          </span><span>name</span><span>=</span><span>"带金箔的头盔"</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>/&gt;</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>&lt;</span><span>Item</span></div></div><div><div><div>22</div></div><div><span>          </span><span>isPacked</span><span><span>=</span><span>{</span></span><span>false</span><span>}</span></div></div><div><div><div>23</div></div><div><span>          </span><span>name</span><span>=</span><span>"Tam 的照片"</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>/&gt;</span></div></div><div><div><div>25</div></div><div><span><span>      </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>&lt;/</span><span>section</span><span>&gt;</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果 <code>isPacked</code> 属性是 <code>true</code>，这段代码会<strong>返回一个不一样的 JSX</strong>。</p><p>在一些情况下，你不想有任何东西进行渲染。比如，你不想显示已经打包好的物品。但一个组件必须返回一些东西。这种情况下，你可以直接返回 <code>null</code>。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if</span><span><span> (</span><span>isPacked</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>return</span><span> &lt;</span><span>li</span><span> </span><span>className</span><span>=</span><span>"item"</span><span><span>&gt;</span><span>{</span><span>name</span><span>}</span><span>&lt;/</span></span><span>li</span><span>&gt;;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>三目运算符（<code>? :</code>）<a href="#三目运算符-"><span>#</span></a></h3><p>JavaScript 有一种紧凑型语法来实现条件判断表达式——<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Conditional_Operator" target="_blank">条件运算符</a> 又称“三目运算符”。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>return</span><span> (</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span> </span><span>className</span><span>=</span><span>"item"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{</span><span>isPacked</span><span> </span><span>?</span><span> </span><span>name</span><span> </span><span>+</span><span> </span><span>' ✅'</span><span><span> </span><span>:</span><span> </span><span>name</span><span>}</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>或许你认为第一个可能会创建两个不同的 <code>&lt;li&gt;</code> “实例”。但 JSX 元素不是“实例”，因为它们没有内部状态也不是真实的 DOM 节点。它们只是一些简单的描述，所以上面这两个例子事实上是完全相同的。</p></blockquote></section><section><h3>与运算符（<code>&amp;&amp;</code>）<a href="#与运算符"><span>#</span></a></h3><p>你会遇到的另一个常见的快捷表达式是 [JavaScript 逻辑与（<code>&amp;&amp;</code>）运算符](<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Logical_AND#:~:text=The" target="_blank">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Logical_AND#:~:text=The</a> logical AND ( %26%26 ) operator,it returns a Boolean value.)。在 React 组件里，通常用在当条件成立时，你想渲染一些 JSX，<strong>或者不做任何渲染</strong>。使用 <code>&amp;&amp;</code>，可以实现仅当 <code>isPacked</code> 为 <code>true</code> 时，渲染勾选符号。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>return</span><span> (</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span> </span><span>className</span><span>=</span><span>"item"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{</span><span>name</span><span>}</span><span> </span><span>{</span><span>isPacked</span><span> </span><span>&amp;&amp;</span><span> </span><span>'✅'</span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Logical_AND" target="_blank">JavaScript &amp;&amp; 表达式</a> 的左侧（我们的条件）为 <code>true</code> 时，它则返回其右侧的值。但条件的结果是 <code>false</code>，则整个表达式会变成 <code>false</code>。在 JSX 里，React 会将 <code>false</code> 视为一个“空值”，就像 <code>null</code> 或者 <code>undefined</code>，这样 React 就不会在这里进行任何渲染。</p><blockquote><p><strong>切勿将数字放在 <code>&amp;&amp;</code> 左侧.</strong></p><p>JavaScript 会自动将左侧的值转换成布尔类型以判断条件成立与否。然而，如果左侧是 <code>0</code>，整个表达式将变成左侧的值（<code>0</code>），React 此时则会渲染 <code>0</code> 而不是不进行渲染。</p><p>例如，一个常见的错误是 <code>messageCount &amp;&amp; &lt;p&gt;New messages&lt;/p&gt;</code>。其原本是想当 <code>messageCount</code> 为 0 的时候不进行渲染，但实际上却渲染了 <code>0</code>。</p><p>为了更正，可以将左侧的值改成布尔类型：<code>messageCount &gt; 0 &amp;&amp; &lt;p&gt;New messages&lt;/p&gt;</code>。</p></blockquote></section></section>
<section><h2>列表循环<a href="#列表循环"><span>#</span></a></h2><p>对于列表项可以通过 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map" target="_blank"><code>map()</code></a> 或 <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter" target="_blank"><code>filter()</code></a> 这样的方法来渲染出一个组件列表。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>people</span><span> } </span><span>from</span><span> </span><span>'./data.js'</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>getImageUrl</span><span> } </span><span>from</span><span> </span><span>'./utils.js'</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>List</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>listItems</span><span> </span><span>=</span><span><span> </span><span>people</span><span>.</span></span><span>map</span><span><span>(</span><span>person</span><span> </span></span><span>=&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;</span><span>li</span><span> </span><span>key</span><span><span>=</span><span>{</span><span>person</span><span>.</span></span><span>id</span><span><span>}</span><span>&gt;</span></span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;</span><span>img</span></div></div><div><div><div>8</div></div><div><span>        </span><span>src</span><span><span>=</span><span>{</span></span><span>getImageUrl</span><span>(</span><span>person</span><span><span>)</span><span>}</span></span></div></div><div><div><div>9</div></div><div><span>        </span><span>alt</span><span><span>=</span><span>{</span><span>person</span><span>.</span></span><span>name</span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>/&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;</span><span>p</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>&lt;</span><span>b</span><span><span>&gt;</span><span>{</span><span>person</span><span>.</span></span><span>name</span><span><span>}</span><span>&lt;/</span></span><span>b</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>{</span><span>' '</span><span><span> </span><span>+</span><span> </span><span>person</span><span>.</span></span><span>profession</span><span><span> </span><span>+</span><span> </span></span><span>' '</span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>          </span></span><span>因</span><span>{</span><span>person</span><span>.</span><span>accomplishment</span><span><span>}</span><span>而闻名世界</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>&lt;/</span><span>p</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>18</div></div><div><span>  </span><span>return</span><span> &lt;</span><span>ul</span><span>&gt;</span><span>{</span><span>listItems</span><span>}</span><span>&lt;/</span><span>ul</span><span>&gt;;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果想让每个列表项都输出多个 DOM 节点而非一个的话，该怎么做呢？</p><p>Fragment 语法的简写形式 <code>&lt;&gt; &lt;/&gt;</code> 无法接受 key 值，所以只能要么把生成的节点用一个 <code>&lt;div&gt;</code> 标签包裹起来，要么使用长一点但更明确的 <code>&lt;Fragment&gt;</code> 写法：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import { Fragment } from 'react';</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// ...</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const listItems = people.map(person =&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;Fragment key={person.id}&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;h1&gt;{person.name}&lt;/h1&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;p&gt;{person.bio}&lt;/p&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&lt;/Fragment&gt;</span></div></div><div><div><div>10</div></div><div><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这里的 Fragment 标签本身并不会出现在 DOM 上，这串代码最终会转换成 <code>&lt;h1&gt;</code>、<code>&lt;p&gt;</code>、<code>&lt;h1&gt;</code>、<code>&lt;p&gt;</code>…… 的列表。</p><section><h3>如何设定 <code>key</code> 值<a href="#如何设定-key-值"><span>#</span></a></h3><p>不同来源的数据往往对应不同的 key 值获取方式：</p><ul>
<li><strong>来自数据库的数据：</strong> 如果你的数据是从数据库中获取的，那你可以直接使用数据表中的主键，因为它们天然具有唯一性。</li>
<li><strong>本地产生数据：</strong> 如果你数据的产生和保存都在本地（例如笔记软件里的笔记），那么你可以使用一个自增计数器，<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Crypto/randomUUID" target="_blank"><code>crypto.randomUUID()</code></a> 或者一个类似 <a href="https://www.npmjs.com/package/uuid" target="_blank"><code>uuid</code></a> 的库来生成 key。</li>
</ul></section><section><h3>key 需要满足的条件<a href="#key-需要满足的条件"><span>#</span></a></h3><ul>
<li><strong>key 值在兄弟节点之间必须是唯一的。</strong> 不过不要求全局唯一，在不同的数组中可以使用相同的 key。</li>
<li><strong>key 值不能改变</strong>，否则就失去了使用 key 的意义！所以千万不要在渲染时动态地生成 key。</li>
</ul></section><section><h3>React 中为什么需要 key？<a href="#react-中为什么需要-key"><span>#</span></a></h3><p>设想一下，假如你桌面上的文件都没有文件名，取而代之的是，你需要通过文件的位置顺序来区分它们———第一个文件，第二个文件，以此类推。也许你也不是不能接受这种方式，可是一旦你删除了其中的一个文件，这种组织方式就会变得混乱无比。原来的第二个文件可能会变成第一个文件，第三个文件会成为第二个文件……</p><p>React 里需要 key 和文件夹里的文件需要有文件名的道理是类似的。它们（key 和文件名）都让我们可以从众多的兄弟元素中唯一标识出某一项（JSX 节点或文件）。而一个精心选择的 key 值所能提供的信息远远不止于这个元素在数组中的位置。即使元素的位置在渲染的过程中发生了改变，它提供的 <code>key</code> 值也能让 React 在整个生命周期中一直认得它。</p><blockquote><p>你可能会想直接把数组项的索引当作 key 值来用，实际上，如果你没有显式地指定 <code>key</code> 值，React 确实默认会这么做。但是数组项的顺序在插入、删除或者重新排序等操作中会发生改变，此时把索引顺序用作 key 值会产生一些微妙且令人困惑的 bug。</p><p>与之类似，请不要在运行过程中动态地产生 key，像是 <code>key={Math.random()}</code> 这种方式。这会导致每次重新渲染后的 key 值都不一样，从而使得所有的组件和 DOM 元素每次都要重新创建。这不仅会造成运行变慢的问题，更有可能导致用户输入的丢失。所以，使用能从给定数据中稳定取得的值才是明智的选择。</p><p>有一点需要注意，组件不会把 <code>key</code> 当作 props 的一部分。Key 的存在只对 React 本身起到提示作用。如果你的组件需要一个 ID，那么请把它作为一个单独的 prop 传给组件： <code>&lt;Profile key={id} userId={id} /&gt;</code>。</p></blockquote></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/cong-ling-kai-shi-de-npm-jiao-cheng/</id>
      <title type="text">从零开始的npm教程</title>
      <published>2025-12-06T02:56:55.000Z</published>
      <updated>2025-12-06T02:56:55.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/cong-ling-kai-shi-de-npm-jiao-cheng/"/>
      <summary type="text">npm 是 Node.js 的标准包管理器，它允许你下载别人写好的代码（称为“包”或 package）直接在你的项目中使用，同时也帮助你管理这些代码之间的 依赖关系 （即你的项目运行所需要的外部代码）。 package.json 是 Node.js 项目中最重要的文件，如果没有它，npm 就不知道你…</summary>
      <content type="html"><![CDATA[<section><h2>npm<a href="#npm"><span>#</span></a></h2><p>npm 是 Node.js 的标准包管理器，它允许你下载别人写好的代码（称为“包”或 package）直接在你的项目中使用，同时也帮助你管理这些代码之间的<strong>依赖关系</strong>（即你的项目运行所需要的外部代码）。</p><section><h3>package.json<a href="#packagejson"><span>#</span></a></h3><p><code>package.json</code>是 Node.js 项目中最重要的文件，如果没有它，npm 就不知道你的项目叫什么，也不知道该下载哪些代码包来帮你的项目运行。</p><p>以下是最基础的 <code>package.json</code> 的模板：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>"name": "my-super-app",</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>"version": "1.0.0",</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>"description": "这是个 npm 的项目",</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>"main": "index.js",</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>"scripts": {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>"test": "echo \"Error: no test specified\" &amp;&amp; exit 1"</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>"dependencies": {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>"lodash": "^4.17.21"</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>"devDependencies": {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>"eslint": "^7.32.0",</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>"author": "你的名字",</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>"license": "ISC"</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h4><code>name </code> 项目名称<a href="#name--项目名称"><span>#</span></a></h4><p>在 npm 中必须遵循一下规则，这个名字将来可能会成为 URL 的一部分：</p><ul>
<li><strong>必须是全小写</strong>：不能有大写字母。</li>
<li><strong>不能有空格</strong>：可以使用连字符（<code>-</code>）或下划线（<code>_</code>）。</li>
<li><strong>不能包含特殊字符</strong>：也就是要是 URL 安全的字符。</li>
</ul></section><section><h4><code>version</code> 版本号<a href="#version-版本号"><span>#</span></a></h4><p>npm 严格遵循一种叫做 <strong>语义化版本 (Semantic Versioning)</strong> 的规范，通常缩写为 <strong>SemVer</strong>。</p><p>版本号通常长这样：<strong><code>1.0.0</code></strong>，由三个数字组成，中间用点隔开。</p><p>我们可以把它们拆解为：<strong><code>主版本号.次版本号.修订号</code> (Major.Minor.Patch)</strong></p>

<table><thead><tr><th><strong>数字位置</strong></th><th><strong>名称</strong></th><th><strong>什么时候改变？</strong></th></tr></thead><tbody><tr><td><strong>1</strong>.0.0</td><td><strong>主版本</strong> (Major)</td><td>当做了<strong>不兼容</strong>的 API 修改时。</td></tr><tr><td>1.<strong>0</strong>.0</td><td><strong>次版本</strong> (Minor)</td><td>当增加了<strong>向下兼容</strong>的新功能时。</td></tr><tr><td>1.0.<strong>0</strong></td><td><strong>修订号</strong> (Patch)</td><td>当进行了<strong>向下兼容</strong>的问题修正 (Fix Bug) 时。</td></tr></tbody></table><p><code>package.json</code>版本号前面的符号（如<code>^</code>和<code>~</code>）是用于指定<strong>版本范围</strong>的，<code>^</code>（插入号）允许更新次要版本和补丁版本（<code>^1.2.3</code>可更新到<code>1.x.x</code>但不能<code>2.0.0</code>），而<code>~</code>（波浪号）只允许更新补丁版本（<code>~1.2.3</code>可更新到<code>1.2.x</code>但不能<code>1.3.0</code>）。</p><ul>
<li><strong><code>^</code> (Caret / 插入号)</strong>：
<ul>
<li><strong>含义</strong>：锁定主版本号，允许次要版本和补丁版本更新。</li>
<li><strong>示例</strong>：<code>^1.2.3</code> 会安装 <code>&gt;=1.2.3</code> 且 <code>&lt;2.0.0</code> 的最新版本。</li>
<li><strong>特殊情况</strong>：对于<code>0.x.x</code>版本（如<code>^0.1.0</code>），<code>^</code>行为与<code>~</code>类似，只允许更新到<code>0.1.x</code>，因为<code>0.x.x</code>阶段变动可能不兼容。</li>
</ul>
</li>
<li><strong><code>~</code> (Tilde / 波浪号)</strong>：
<ul>
<li><strong>含义</strong>：锁定主版本号和次要版本号，只允许更新补丁版本。</li>
<li><strong>示例</strong>：<code>~1.2.3</code> 会安装 <code>&gt;=1.2.3</code> 且 <code>&lt;1.3.0</code> 的最新版本。</li>
</ul>
</li>
<li><strong>无符号 (指定版本)</strong>：
<ul>
<li><strong>含义</strong>：只安装指定的具体版本。</li>
<li><strong>示例</strong>：<code>1.2.3</code> 只会安装<code>1.2.3</code>。</li>
</ul>
</li>
<li><strong><code>\*</code> (星号)</strong>：
<ul>
<li><strong>含义</strong>：匹配任何版本。</li>
<li><strong>示例</strong>：<code>*</code> 或 <code>latest</code> 表示安装最新版本。</li>
</ul>
</li>
<li><strong><code>&lt;</code> , <code>&gt;</code> , <code>&lt;=</code> , <code>&gt;=</code></strong>：
<ul>
<li><strong>含义</strong>：明确版本范围。</li>
<li><strong>示例</strong>：<code>&lt;=3.2.1</code> 表示小于或等于<code>3.2.1</code>。</li>
</ul>
</li>
</ul><p>在下载依赖时，怎么样确定是<code>~</code>还是<code>^</code>？</p><p><strong><code>npm</code> 的默认行为：</strong></p><ul>
<li>
<p><strong>默认使用 <code>^</code> (插入符)。</strong> 自 npm v5 版本以来，当你运行 <code>npm install &lt;package&gt;</code> 时，它会自动在 <code>package.json</code> 中使用 <code>^</code> 符号来记录依赖。</p>
</li>
<li>
<p>如果包的版本号是 <code>0.x.x</code> (即主版本号为 0)，由于 0.x.x 版本被认为不稳定，<code>npm</code> 通常会使用更严格的 <code>~</code> 或直接锁定版本。</p>
</li>
</ul><p>只有在要求<strong>最大程度的稳定性</strong>时，才会手动去使用 <code>~</code> 符号，或者使用 <code>npm install &lt;package&gt; --save-exact</code> 来精确锁定版本号，以覆盖 <code>npm</code> 默认的 <code>^</code> 行为。</p></section><section><h4><code>main</code> 模块入口<a href="#main-模块入口"><span>#</span></a></h4><p>这是这个包的 <strong>“公开 API 入口”</strong>。它告诉 Node.js：当其他开发者安装并尝试使用你的包时，应该从哪个文件开始执行。</p><p>在这个例子中，<code>"main": "index.js"</code> 表示当有人在自己的代码中写下 <code>const myApp = require('my-super-app');</code> 时，Node.js 就会去加载并返回你的 <code>index.js</code> 文件导出的内容。</p><p>如果没有这个字段，Node.js 默认会去找 <code>index.js</code>，但明确指定可以确保其他模块正确调用你的库。</p></section><section><h4><code>exports</code> 模块入口<a href="#exports-模块入口"><span>#</span></a></h4><p>随着 JavaScript 模块系统从 CommonJS（使用 <code>require()</code>）过渡到 ES Modules（使用 <code>import</code>），开发者需要一种方式来告诉 Node.js 和打包工具：“如果你是使用 <code>import</code> 导入我的包，请给我 ES Module 格式的文件；如果你是使用 <code>require()</code>，请给我 CommonJS 格式的文件。”</p><p><code>exports</code> 字段完美解决了这个问题，它提供了 <strong>“条件导出” (Conditional Exports)</strong> 的能力。</p>

<table><thead><tr><th><strong>字段</strong></th><th><strong>作用</strong></th><th><strong>限制</strong></th></tr></thead><tbody><tr><td><strong><code>main</code></strong></td><td>传统入口。只能指向一个文件。</td><td>无法区分 <code>require</code> 和 <code>import</code>。</td></tr><tr><td><strong><code>exports</code></strong></td><td><strong>现代入口。</strong> 可以指向多个文件，并根据条件选择。</td><td>现代工具优先使用它。</td></tr></tbody></table><p>这是一个常见的 <code>exports</code> 定义，它允许你的包支持两种模块格式：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"name"</span><span>: </span><span>"my-modern-app"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"exports"</span><span>: {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"import"</span><span>: </span><span>"./dist/index.mjs"</span><span>,    </span><span>// 针对 ES Module (import)</span></div></div><div><div><div>5</div></div><div><span>    </span><span>"require"</span><span>: </span><span>"./dist/index.cjs"</span><span>,   </span><span>// 针对 CommonJS (require)</span></div></div><div><div><div>6</div></div><div><span>    </span><span>"default"</span><span>: </span><span>"./dist/index.js"</span><span>     </span><span>// 如果上述条件都不满足</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>在 <code>package.json</code> 中，如果同时设置了 <code>main</code> 和 <code>exports</code> 字段，官方规则如下：</p><ol>
<li><strong><code>exports</code> 优先：</strong> 只要 <code>exports</code> 字段存在，Node.js 的最新版本和现代打包工具（如 Webpack、Rollup、Vite）就会<strong>优先读取并使用 <code>exports</code> 字段</strong>，而完全<strong>忽略</strong> <code>main</code> 字段。</li>
<li><strong><code>main</code> 回退：</strong> 只有在 <code>exports</code> 字段不存在，或者运行环境是<strong>旧版本的 Node.js</strong> 或<strong>不支持 <code>exports</code> 的旧工具</strong>时，<code>main</code> 字段才会起作用。</li>
</ol></blockquote></section><section><h4><code>dependencies</code> (生产环境依赖)<a href="#dependencies-生产环境依赖"><span>#</span></a></h4><p>这里列出的包是项目在 <strong>运行</strong> 时<strong>必须</strong>需要的。例如：React, Express, Vue 等。运行 <code>npm install</code> 时总是会被下载。</p><p>在npm v5 版本后 <code>npm install &lt;package&gt;</code> 会自动下载到 dependencies 中，等同于 <code>npm install &lt;package&gt; --save</code></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--save</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--S</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>通过<code>@</code> 符号的作用是用来<strong>指定你想要安装的包的特定版本号或标签</strong>。</p><p><code>npm install lodash@4.17.21</code>：安装 <code>lodash</code> 库的精确版本 <code>4.17.21</code>。</p><p><code>npm install react@beta</code>：安装 <code>react</code> 库的 <code>beta</code> 预发布版本。</p><p><code>npm install vue@latest</code>：安装 <code>vue</code> 库的最新稳定版本。 等用于 <code>npm install vue</code></p></section><section><h4><code>devDependencies</code> (开发环境依赖)<a href="#devdependencies-开发环境依赖"><span>#</span></a></h4><p>这里列出的包只在你进行<strong>开发、测试或构建</strong>项目时需要。例如：测试工具 (Jest)、代码检查工具 (ESLint) 或打包工具 (Webpack/Vite) 等。运行 <code>npm install --production</code> 时会被跳过。执行下面命令会下载到 devDependencies</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--save-dev</span></div></div><div><div><div>2</div></div><div><span># 简写</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-D</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>scripts</code> 脚本命令<a href="#scripts-脚本命令"><span>#</span></a></h4><p><code>scripts</code> 部分允许你定义一些自定义的命令，你可以用 <code>npm run &lt;命令名&gt;</code> 来执行它们。目的是把一个复杂的命令行指令封装成一个简单的自定义命令。</p><p>而且把复杂命令封装成简单脚本的主要好处是：</p><ol>
<li><strong>简化操作</strong>：开发者不用记住长串复杂的命令。</li>
<li><strong>跨平台一致性</strong>：无论在 Windows、macOS 还是 Linux 上，执行 <code>npm run build</code> 的结果都是一致的。</li>
<li><strong>路径安全</strong>：<code>npm</code> 会自动确保你在脚本中运行的程序（比如 <code>vite</code> 或 <code>webpack</code>）是正确的路径，你不需要担心它们是否被添加到系统的 <code>PATH</code> 环境变量中。</li>
</ol><p>在一个典型的 Vue 3 项目（使用 Vite 作为构建工具）中，<code>scripts</code> 部分通常是这样的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// ... 其他字段 ...</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"scripts"</span><span>: {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"dev"</span><span>: </span><span>"vite"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>"build"</span><span>: </span><span>"vue-tsc --noEmit &amp;&amp; vite build"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>"preview"</span><span>: </span><span>"vite preview"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>"lint"</span><span>: </span><span>"eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>

<table><thead><tr><th><strong>命令</strong></th><th><strong>实际运行</strong></th><th><strong>用途</strong></th></tr></thead><tbody><tr><td><code>npm run dev</code></td><td><code>vite</code></td><td>启动本地开发服务器。</td></tr><tr><td><code>npm run build</code></td><td><code>vue-tsc... &amp;&amp; vite build</code></td><td>执行类型检查，然后构建生产环境文件。</td></tr><tr><td><code>npm run lint</code></td><td><code>eslint...</code></td><td>自动修复代码格式和潜在错误。</td></tr></tbody></table><p>其中<code>npm run build </code>中的 <code> &amp;&amp;</code> 是要求两个命令<strong>按条件顺序执行</strong>。只有前面的命令执行<strong>成功</strong>（退出代码为 0）时，才会继续执行后面的命令。如果第一个命令失败了，整个脚本就会立即停止。</p><blockquote><p>为什么有些脚本（比如 <code>start</code> 和 <code>test</code>）可以直接使用 <strong><code>npm start</code></strong> 和 <strong><code>npm test</code></strong> 运行，而不需要像 <code>build</code> 或 <code>lint</code> 那样使用 <strong><code>npm run build</code></strong>？</p><p>原因是 <code>start</code>、<code>test</code> 等是 <code>npm</code> 设定的一些<strong>标准生命周期脚本 (Standard Lifecycle Scripts)</strong>。它们是几乎所有 Node.js 项目在开发过程中都会用到的<strong>核心操作</strong>，例如：</p><ul>
<li><code>npm start</code>: 启动项目。</li>
<li><code>npm test</code>: 运行测试。</li>
<li><code>npm install</code>: 安装依赖。</li>
</ul><p>为了方便开发者，<code>npm</code> 允许用户在运行这些<strong>标准脚本</strong>时省略 <code>run</code> 关键字，这纯粹是为了<strong>节省时间</strong>和<strong>简化操作</strong>。而像 <code>build</code> 或 <code>lint</code> 这样的自定义脚本则必须使用 <code>npm run</code>。</p></blockquote>

<table><thead><tr><th><strong>类别</strong></th><th><strong>命令/别名</strong></th><th><strong>完整命令</strong></th><th><strong>用途/含义</strong></th></tr></thead><tbody><tr><td><strong>初始化</strong></td><td><code>npm init</code></td><td><code>npm init</code></td><td>初始化一个新项目，创建 <code>package.json</code> 文件。</td></tr><tr><td><strong>安装</strong></td><td><code>npm install</code> / <code>npm i</code></td><td><code>npm install</code></td><td>下载 <code>package.json</code> 中所有依赖，安装到 <code>node_modules</code>。</td></tr><tr><td></td><td><code>npm i &lt;package&gt;</code></td><td><code>npm install &lt;package&gt;</code></td><td>默认安装为<strong>生产依赖</strong> (<code>dependencies</code>)。</td></tr><tr><td></td><td><code>npm i -D &lt;package&gt;</code></td><td><code>npm install &lt;package&gt; --save-dev</code></td><td>安装为<strong>开发依赖</strong> (<code>devDependencies</code>)。</td></tr><tr><td><strong>运行</strong></td><td><code>npm run &lt;script&gt;</code></td><td><code>npm run &lt;script&gt;</code></td><td>运行 <code>package.json</code> 中定义的自定义脚本。</td></tr><tr><td></td><td><code>npm start</code></td><td><code>npm run start</code></td><td>运行标准 <code>start</code> 脚本 (可省略 <code>run</code>)。</td></tr><tr><td><strong>管理</strong></td><td><code>npm uninstall &lt;package&gt;</code></td><td><code>npm uninstall &lt;package&gt;</code></td><td>卸载依赖，并从 <code>package.json</code> 中移除记录。</td></tr><tr><td></td><td><code>npm update</code></td><td><code>npm update</code></td><td>更新已安装的包到允许的最新版本。</td></tr></tbody></table><p><code> npm install &lt;package&gt; --global</code>简写<code>-g</code>是将包安装到<strong>系统路径</strong>，作为全局命令行工具使用。</p></section></section><section><h3>package-lock.json<a href="#package-lockjson"><span>#</span></a></h3><p><code>package.json</code> 记录的是依赖的<strong>版本范围</strong>（例如 <code>^1.0.0</code>），允许 Minor 和 Patch 升级。这就意味着，今天你安装的版本和三个月后新同事安装的版本可能不一样。</p><p>而 <code>package-lock.json</code> 记录的是一个包及其<strong>所有子依赖</strong>（依赖树）在安装时的<strong>精确版本号</strong>、<strong>下载地址</strong>和<strong>完整性校验值 (integrity hash)</strong>。</p><p>它的主要目标是：<strong>保证可重复构建 (Repeatable Builds)</strong>。无论何时何地运行 <code>npm install</code>，只要有 <code>package-lock.json</code> 存在，<code>node_modules</code> 文件夹的内容就必须完全一致。</p><p>当你在项目中共存 <code>package.json</code> 和 <code>package-lock.json</code> 两个文件并运行 <code>npm install</code> 时，<code>npm</code> 的行为遵循一个严格的优先流程：</p><ol>
<li><strong>检查锁定文件：</strong> <code>npm</code> 首先会查找 <code>package-lock.json</code>。</li>
<li><strong>强制精确安装：</strong> 如果找到该文件，<code>npm</code> 会<strong>严格按照</strong>文件中记录的精确版本号、下载地址（<code>resolved</code> 字段）和完整性校验值（<code>integrity</code> 字段）来安装包。</li>
<li><strong>忽略范围：</strong> 在这个阶段，<code>npm</code> 会<strong>忽略</strong> <code>package.json</code> 中定义的版本范围 (<code>^</code> 或 <code>~</code>) 所允许的任何较新版本。</li>
</ol><p>假设 <code>package.json</code> 允许范围是 <code>^1.0.0</code>，而 <code>package-lock.json</code> 锁定的精确版本是 <code>1.0.5</code>。npm 会优先下载 <code>package-lock.json</code>中的版本。</p><p><code>package-lock.json</code> 的 <code>integrity</code>  （完整性）字段的作用是提供 <strong>内容校验</strong> 和 <strong>安全保障</strong>：</p><ol>
<li><strong>哈希值：</strong> <code>integrity</code> 字段记录了包文件的加密哈希值（例如 SHA-512）。</li>
<li><strong>内容验证：</strong> <code>npm</code> 下载包后，会立即计算下载文件的哈希值。</li>
<li><strong>停止安装：</strong> 如果计算出的哈希值与 <code>package-lock.json</code> 中记录的哈希值不匹配，则说明文件在传输过程中<strong>可能被篡改</strong>，<code>npm</code> 会拒绝安装以防止潜在的恶意代码注入。</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jiang-hexo-ti-jiao-dao-cang-ku/</id>
      <title type="text">将 Hexo 提交到仓库</title>
      <published>2025-11-26T18:27:34.000Z</published>
      <updated>2025-11-26T18:27:34.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jiang-hexo-ti-jiao-dao-cang-ku/"/>
      <summary type="text">这是一个 Hexo 双仓库管理问题（一个是用于存放 静态网页 的 xxx.github.io ，一个是用于存放 源代码 的 hexo code ）。 提交不上去是因为 Git 配置冲突 或者 .gitignore 设置不当 造成的。</summary>
      <content type="html"><![CDATA[<section><h2>将 Hexo 提交到仓库<a href="#将-hexo-提交到仓库"><span>#</span></a></h2><p>这是一个 Hexo 双仓库管理问题（一个是用于存放<strong>静态网页</strong>的 <code>xxx.github.io</code>，一个是用于存放<strong>源代码</strong>的 <code>hexo-code</code>）。</p><p>提交不上去是因为<strong>Git 配置冲突</strong>或者**<code>.gitignore</code> 设置不当**造成的。</p><section><h3>创建 <code>.gitignore</code> 文件<a href="#创建-gitignore-文件"><span>#</span></a></h3><p>Hexo 部署和源码备份最常见的问题就是把不该传的文件传了，或者该传的文件被忽略了。</p><p>打开或在根目录下新建 <code>.gitignore</code> 文件，确保里面<strong>必须</strong>包含以下内容。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.DS_Store</span></div></div><div><div><div>2</div></div><div><span>Thumbs.db</span></div></div><div><div><div>3</div></div><div><span>db.json</span></div></div><div><div><div>4</div></div><div><span>*.log</span></div></div><div><div><div>5</div></div><div><span>node_modules/</span></div></div><div><div><div>6</div></div><div><span>public/</span></div></div><div><div><div>7</div></div><div><span>.deploy_git/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><strong><code>public/</code> 和 <code>.deploy_git/</code></strong>：这两个文件夹是 <code>hexo d</code> 生成的静态文件和部署用的 git 仓库，<strong>绝对不能</strong>提交到你的源码仓库 <code>hexo-code</code> 中，否则会造成严重的嵌套冲突。</li>
<li><strong><code>node_modules/</code></strong>：这是依赖包，体积很大且不需要备份，只需备份 <code>package.json</code> 即可。</li>
</ul></section><section><h3>处理主题（Themes）文件夹的 Git 冲突<a href="#处理主题themes文件夹的-git-冲突"><span>#</span></a></h3><p>这是最容易导致“无法提交”或“提交后文件夹是空的”的原因。</p><p>如果你下载的主题是通过 <code>git clone</code> 下载到 <code>themes/</code> 目录下的，那么该主题文件夹里会有一个隐藏的 <code>.git</code> 文件夹。</p><p>**解决方法：**进入 <code>themes/你的主题名/</code> 目录。删掉主题下的 <code>.git</code> 文件夹，不要删掉根目录的）。</p><p>这样做是为了让主仓库把主题文件当作普通文件来管理，而不是当作一个子模块（Submodule）。</p></section><section><h3>生成 SSH 公钥连接到 GitHub<a href="#生成-ssh-公钥连接到-github"><span>#</span></a></h3><section><h4>检查并生成 SSH 密钥<a href="#检查并生成-ssh-密钥"><span>#</span></a></h4><p>请在你的 <strong>Git Bash</strong> (或者终端) 中依次执行以下命令：</p><ol>
<li>
<p><strong>生成新密钥</strong>（请直接复制下方命令，<strong>将其中的 <code>你的邮箱</code> 换成你 GitHub 注册的邮箱</strong>）：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh-keygen -t ed25519 -C "email@example.com"</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>一路回车</strong>：</p>
<ul>
<li>系统会问你保存路径 -&gt; <strong>按回车</strong>（默认即可）。</li>
<li>系统会问你是否覆盖 -&gt; 如果提示 overwrite，输入 <code>y</code> 然后回车；如果没有提示，直接下一步。</li>
<li>系统会问你 Passphrase（密码） -&gt; <strong>按回车</strong>（留空，不要设密码，否则以后每次部署都要输密码）。</li>
<li>再次确认密码 -&gt; <strong>按回车</strong>。</li>
</ul>
<p>直到看见一个矩形的字符画（Randomart image），说明生成成功了。</p>
</li>
</ol></section><section><h4>获取公钥内容<a href="#获取公钥内容"><span>#</span></a></h4><p>继续在终端输入以下命令，查看你的公钥内容：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cat ~/.ssh/id_ed25519.pub</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>终端会显示以 <code>ssh-ed25519</code> 开头的一长串字符。<strong>请把这一整段内容（包括开头的 ssh-ed25519 和结尾的邮箱）全部复制下来。</strong></p></section><section><h4>添加到 GitHub<a href="#添加到-github"><span>#</span></a></h4><ol>
<li>打开浏览器，登录你的 GitHub 账号。</li>
<li>点击右上角头像 -&gt; <strong>Settings</strong> (设置)。</li>
<li>在左侧菜单栏找到 <strong>SSH and GPG keys</strong>。</li>
<li>点击绿色的 <strong>New SSH key</strong> 按钮。</li>
<li><strong>Title</strong>：随便填（例如：<code>My Laptop</code>）。</li>
<li><strong>Key</strong>：<strong>粘贴</strong>你在第二步复制的那一长串内容。</li>
<li>点击 <strong>Add SSH key</strong>。</li>
</ol></section><section><h4>验证<a href="#验证"><span>#</span></a></h4><p>回到你的 <strong>Git Bash</strong> 终端，输入以下命令测试连接：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-T</span><span> </span><span>git@github.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>
<p>如果它问 <code>Are you sure...</code>，再次输入 <code>yes</code> 并回车。</p>
</li>
<li>
<p><strong>成功的标志</strong>：你会看到这样一句话：</p>
<blockquote><p><code>Hi &lt;你的用户名&gt;! You've successfully authenticated, but GitHub does not provide shell access.</code></p></blockquote>
</li>
</ul></section><section><h4>重新部署 Hexo<a href="#重新部署-hexo"><span>#</span></a></h4><p>请回到 Hexo 目录，再次尝试：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>就能顺利上传了。</p></section></section><section><h3>清除凭证<a href="#清除凭证"><span>#</span></a></h3><section><h4>清除本地的 GitHub 身份凭证（最关键）<a href="#清除本地的-github-身份凭证最关键"><span>#</span></a></h4><p>你的电脑上有两把“钥匙”可以访问你的 GitHub 账户：<strong>SSH 私钥</strong>和<strong>Git 凭证管理器</strong>。</p><ol>
<li>删除 SSH 私钥文件</li>
</ol><p>​	你之前为了部署 Hexo 而创建的 SSH 密钥对，其中私钥文件 (<code>id_ed25519</code>) 存放在你的用户主目录下。</p><ul>
<li><strong>路径：</strong> <code>C:\Users\你的用户名\.ssh\</code> 文件夹</li>
<li><strong>需要删除的文件：</strong>
<ul>
<li><code>id_ed25519</code> (这是你的<strong>私钥</strong>，务必删除！)</li>
<li><code>id_ed25519.pub</code> (这是公钥，删除它也无妨，但私钥是关键)</li>
<li><code>known_hosts</code> (记录了连接过的服务器指纹，删除可清理连接记录)</li>
</ul>
</li>
</ul><ol>
<li>清除 Git 凭证缓存</li>
</ol><p>​	如果你使用 HTTPS 方式克隆仓库，或者在部署时输入过 GitHub 的用户名和密码/个人访问令牌 (PAT)，系统可能会将其缓存在 Windows 的凭证管理器中。</p><ol>
<li>打开 Windows 搜索栏，搜索 <strong>“凭证管理器”</strong> (Credential Manager)。</li>
<li>选择 <strong>“Windows 凭证”</strong> (Windows Credentials)。</li>
<li>在“普通凭证” (Generic Credentials) 部分，查找任何以 <code>git:https://github.com</code> 或 <code>github.com</code> 开头的条目。</li>
<li>找到后点击并选择 <strong>“移除”</strong> (Remove) 或 <strong>“删除”</strong> (Delete)。</li>
</ol></section><section><h4>从 GitHub 账户中删除公钥（远程操作）<a href="#从-github-账户中删除公钥远程操作"><span>#</span></a></h4><p>即使本地的私钥文件被删除，为了防止任何人通过其他方式恢复该文件，你还需要从 GitHub 账户中移除对应的公钥。这样，即使私钥泄露，它也无法用于访问你的 GitHub 账户。</p><ol>
<li>打开浏览器，登录你的 GitHub 账户。</li>
<li>点击右上角头像，进入 <strong>Settings</strong> (设置)。</li>
<li>在左侧菜单栏找到 <strong>SSH and GPG keys</strong>。</li>
<li>找到你之前添加的、用于这台电脑的 SSH Key 确认删除</li>
</ol></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/cong-xian-you-git-cang-ku-qie-huan-dao-ling-yi-ge-cang-ku/</id>
      <title type="text">从现有git仓库切换到另一个仓库</title>
      <published>2025-11-26T06:51:08.000Z</published>
      <updated>2025-11-26T06:51:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/cong-xian-you-git-cang-ku-qie-huan-dao-ling-yi-ge-cang-ku/"/>
      <summary type="text">此方法会 保留 原有的 origin （指向 url1 ），同时 新增 一个指向 url2 的远程仓库。这样本地仓库就可以同时与两个远程仓库进行通信。</summary>
      <content type="html"><![CDATA[<section><h2>从现有仓库切换到另一个仓库<a href="#从现有仓库切换到另一个仓库"><span>#</span></a></h2><section><h3>方法一：添加新的远程仓库<a href="#方法一添加新的远程仓库"><span>#</span></a></h3><p>此方法会<strong>保留</strong>原有的 <code>origin</code>（指向 <code>url1</code>），同时<strong>新增</strong>一个指向 <code>url2</code> 的远程仓库。这样本地仓库就可以同时与两个远程仓库进行通信。</p><section><h4>步骤 1：检查本地仓库配置<a href="#步骤-1检查本地仓库配置"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>remote</span><span> </span><span>-v</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预期输出类似：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>origin</span><span>  </span><span>url1</span><span> (fetch)</span></div></div><div><div><div>2</div></div><div><span>origin</span><span>  </span><span>url1</span><span> (push)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>步骤 2：添加新的远程仓库<a href="#步骤-2添加新的远程仓库"><span>#</span></a></h4><p>为新的仓库 <code>url2</code> 指定名称，例如 <code>new_origin</code>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>remote</span><span> </span><span>add</span><span> </span><span>new_origin</span><span> </span><span>url2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p><strong>注意：</strong> 将 <code>new_origin</code> 替换为自定义名称，将 <code>url2</code> 替换为新仓库的实际 URL。</p></blockquote></section><section><h4>步骤 3：验证配置<a href="#步骤-3验证配置"><span>#</span></a></h4><p>再次执行 <code>git remote -v</code>，确认两个远程仓库均已配置：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>remote</span><span> </span><span>-v</span></div></div><div><div><div>2</div></div><div><span>new_origin</span><span>  </span><span>url2</span><span> (fetch)</span></div></div><div><div><div>3</div></div><div><span>new_origin</span><span>  </span><span>url2</span><span> (push)</span></div></div><div><div><div>4</div></div><div><span>origin</span><span>      </span><span>url1</span><span> (fetch)</span></div></div><div><div><div>5</div></div><div><span>origin</span><span>      </span><span>url1</span><span> (push)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>步骤 4：推送代码到新仓库<a href="#步骤-4推送代码到新仓库"><span>#</span></a></h4><p>将代码推送到新的远程仓库 (<code>new_origin</code>)：</p><p><strong>推送当前分支（如 <code>main</code> 或 <code>master</code>）：</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 假设主分支为 'main'</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>new_origin</span><span> </span><span>main</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>推送所有本地分支：</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>new_origin</span><span> </span><span>--all</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>推送所有标签：</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>new_origin</span><span> </span><span>--tags</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>设置默认上游分支：</strong></p><p>若希望未来在 <code>main</code> 分支执行 <code>git push</code> 时默认推送到 <code>new_origin</code>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># -u 参数表示 --set-upstream，将本地 main 分支关联到 new_origin/main</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>-u</span><span> </span><span>new_origin</span><span> </span><span>main</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>方法二：修改现有 <code>origin</code> 配置<a href="#方法二修改现有-origin-配置"><span>#</span></a></h3><p>如果<strong>不再需要 <code>url1</code></strong>，只需将 <code>origin</code> 指向 <code>url2</code>，可采用此方法。</p><section><h4>步骤 1：修改 <code>origin</code> 的 URL<a href="#步骤-1修改-origin-的-url"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>remote</span><span> </span><span>set-url</span><span> </span><span>origin</span><span> </span><span>url2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>步骤 2：验证修改<a href="#步骤-2验证修改"><span>#</span></a></h4><p>执行 <code>git remote -v</code>，确认 <code>origin</code> 已指向 <code>url2</code>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>remote</span><span> </span><span>-v</span></div></div><div><div><div>2</div></div><div><span>origin</span><span>  </span><span>url2</span><span> (fetch)</span></div></div><div><div><div>3</div></div><div><span>origin</span><span>  </span><span>url2</span><span> (push)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>步骤 3：推送代码<a href="#步骤-3推送代码"><span>#</span></a></h4><p>常规推送操作将自动指向 <code>url2</code>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 推送 main 分支并设置追踪</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>-u</span><span> </span><span>origin</span><span> </span><span>main</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 推送所有分支</span></div></div><div><div><div>5</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>--all</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 推送所有标签</span></div></div><div><div><div>8</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>--tags</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3><code>git status</code> 显示机制说明<a href="#git-status-显示机制说明"><span>#</span></a></h3><p><strong><code>git status</code> 命令仅比较「本地工作目录」与「本地 Git 仓库的最后一次提交」之间的差异。</strong></p><p>该命令<strong>不会</strong>检查远程仓库状态，无论远程仓库为空、过时还是新仓库。</p><p>执行 <code>git status</code> 时，Git 在本地进行以下比较：</p><ol>
<li><strong>工作目录</strong>：实际可见且可编辑的文件</li>
<li><strong>本地快照 (HEAD)</strong>：最后一次 <code>git commit</code> 时存储的版本</li>
</ol><p>“modified” 状态表示：工作目录中的文件与最后一次提交的版本存在差异。</p><p>此过程<strong>完全在本地完成</strong>，无需网络连接，也不涉及远程仓库通信。</p><p>后续提交操作会将所有本地提交推送到远程仓库。</p></section><section><h3>确定主分支的方法<a href="#确定主分支的方法"><span>#</span></a></h3><section><h4>方法一：检查当前所在分支<a href="#方法一检查当前所在分支"><span>#</span></a></h4><p>执行 <code>git branch</code> 查看所有本地分支列表：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>branch</span></div></div><div><div><div>2</div></div><div><span>  </span><span>feature-A</span></div></div><div><div><div>3</div></div><div><span>* main</span></div></div><div><div><div>4</div></div><div><span>  </span><span>hotfix-123</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>标有星号 <code>*</code> 的分支（示例中为 <code>main</code>）为<strong>当前所在分支</strong></li>
<li>在未主动切换分支的情况下，当前分支通常为主分支</li>
</ul></section><section><h4>方法二：检查远程仓库默认分支<a href="#方法二检查远程仓库默认分支"><span>#</span></a></h4><p>此方法显示配置的 <code>origin</code> 仓库自身认定的默认分支。</p><p>执行 <code>git remote show origin</code>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>remote</span><span> </span><span>show</span><span> </span><span>origin</span></div></div><div><div><div>2</div></div><div><span>* remote origin</span></div></div><div><div><div>3</div></div><div><span>  </span><span>Fetch</span><span> </span><span>URL:</span><span> </span><span>url2</span></div></div><div><div><div>4</div></div><div><span>  </span><span>Push</span><span>  </span><span>URL:</span><span> </span><span>url2</span></div></div><div><div><div>5</div></div><div><span>  </span><span>HEAD</span><span> </span><span>branch:</span><span> </span><span>master</span><span>  &lt;</span><span>----</span><span> </span><span>默认分支标识</span></div></div><div><div><div>6</div></div><div><span>  </span><span>Remote</span><span> </span><span>branches:</span></div></div><div><div><div>7</div></div><div><span>    </span><span>main</span><span> </span><span>tracked</span></div></div><div><div><div>8</div></div><div><span>  </span><span>Local</span><span> </span><span>branch</span><span> </span><span>configured</span><span> </span><span>for</span><span> </span><span>'git pull':</span></div></div><div><div><div>9</div></div><div><span>    </span><span>main</span><span> </span><span>merges</span><span> </span><span>with</span><span> </span><span>remote</span><span> </span><span>main</span></div></div><div><div><div>10</div></div><div><span>  </span><span>Local</span><span> </span><span>ref</span><span> </span><span>configured</span><span> </span><span>for</span><span> </span><span>'git push':</span></div></div><div><div><div>11</div></div><div><span>    </span><span>main</span><span> </span><span>pushes</span><span> </span><span>to</span><span> </span><span>main</span><span> (up </span><span>to</span><span> </span><span>date</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>查看 <code>HEAD branch:</code> 行显示的名称（示例中为 <code>master</code>），此为远程仓库的默认分支，通常即为主分支。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>* remote origin</span></div></div><div><div><div>2</div></div><div><span>  </span><span>Fetch</span><span> </span><span>URL:</span><span> </span><span>http://xxx.git</span></div></div><div><div><div>3</div></div><div><span>  </span><span>Push</span><span>  </span><span>URL:</span><span> </span><span>http://xxx.git</span></div></div><div><div><div>4</div></div><div><span>  </span><span>HEAD</span><span> </span><span>branch:</span><span> (unknown)</span></div></div><div><div><div>5</div></div><div><span>  </span><span>Remote</span><span> </span><span>branch:</span></div></div><div><div><div>6</div></div><div><span>    </span><span>refs/remotes/origin/master</span><span> </span><span>stale</span><span> (use </span><span>'git remote prune'</span><span> </span><span>to</span><span> </span><span>remove</span><span>)</span></div></div><div><div><div>7</div></div><div><span>  </span><span>Local</span><span> </span><span>branch</span><span> </span><span>configured</span><span> </span><span>for</span><span> </span><span>'git pull':</span></div></div><div><div><div>8</div></div><div><span>    </span><span>master</span><span> </span><span>merges</span><span> </span><span>with</span><span> </span><span>remote</span><span> </span><span>master</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>HEAD branch: (unknown)</code> 表示新仓库中<strong>不存在任何分支</strong>，Git 无法确定默认分支。</p><p>从输出信息可确认<strong>本地主分支为 <code>master</code></strong>：</p><ol>
<li><code>Remote branch: ... origin/master stale</code>：表示本地曾追踪名为 <code>origin/master</code> 的分支（来自 <code>url1</code>）</li>
<li><code>Local branch configured for 'git pull': master merges with remote master</code>：明确显示本地存在 <code>master</code> 分支</li>
</ol><p>将<strong>本地 <code>master</code> 分支</strong>推送到<strong>空的远程仓库 <code>origin</code></strong>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>-u</span><span> </span><span>origin</span><span> </span><span>master</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>此命令执行两个操作：</p><ol>
<li><code>git push origin master</code>：将本地 <code>master</code> 分支的所有提交推送到 <code>origin</code>（即 <code>url2</code>）</li>
<li><code>-u</code>（或 <code>--set-upstream</code>）：建立追踪关系，配置本地 <code>master</code> 分支与 <code>origin/master</code> 的对应关系</li>
</ol></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/zui-yu-fa/</id>
      <title type="text">《罪与罚》</title>
      <published>2025-11-26T06:49:08.000Z</published>
      <updated>2025-11-26T06:49:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/zui-yu-fa/"/>
      <summary type="text">这是继《钢铁是怎样炼成的》后阅读的第二本俄国文学，或许是第一本？本书作成时沙俄还未推翻罢。 和我印象中的俄国文学一样，光是开篇冗长的人名以及各种昵称都快将我劝退了，直到拉斯克尔尼科夫将放贷的老太婆谋杀后，我才提起兴趣罢。</summary>
      <content type="html"><![CDATA[<section><h2>《罪与罚》<a href="#罪与罚"><span>#</span></a></h2><p>这是继《钢铁是怎样炼成的》后阅读的第二本俄国文学，或许是第一本？本书作成时沙俄还未推翻罢。</p><p>和我印象中的俄国文学一样，光是开篇冗长的人名以及各种昵称都快将我劝退了，直到拉斯克尔尼科夫将放贷的老太婆谋杀后，我才提起兴趣罢。</p><p>前文描写拉斯克尔尼科夫的穷困潦倒，但又忍不住帮助他人的情节，我便草草推断作者想凸显社会的险恶迫使高尚善良的人走向歪路，借以批判当时的俄国社会。和我想的一样，杜尼娅为了哥哥，选择嫁给富有的卢仁，拉斯克尔尼科夫却因为贫穷无能为力，被迫走上犯罪的道路。但是这一切的推论都是建立在拉斯克尔尼科夫是一个伟光正的角色身上，倘若他不是纯粹善良的人，又该如何呢？</p><p>直到波尔菲里和拉斯克尔尼科夫的第一次会谈解答了我的疑问，拉斯克尔尼科夫认为人类分为两类，一类是遵循法律、循规蹈矩的普通人；另一类是自命不凡，认为自己可以凌驾于法律之上肆意剥削他人。拉斯克尔尼科夫不愿做一个甘愿做逆来顺受的平凡的人，不然就如同马尔美拉陀夫最后落得被马踩死的悲惨结局。拉斯克尔尼科夫愤世嫉俗，自命不凡，他认为自己可以成为拿破仑，可以成为审判一切罪恶的法官。然而这类人若是肆意妄为最终会成为卢仁，卑鄙无耻，为非作歹。拉斯克尔尼科夫避免妹妹杜尼娅嫁给卢仁，也是代表他内心并不完全认同这类人。</p><p>拉斯克尔尼科夫后续一直试图用这套理论为自己辩解，然而身体却很诚实的发烧、做噩梦，反复生病。正是这种身体与思想的不协调，被迫接受自己是一个普通人的事实。斯维德里加伊洛夫就是另一个拉斯克尔尼科夫，他骄奢淫欲，妄图侵占杜尼娅；而又富有同情，救助索尼娅一家，在最后依旧逃不过良心的谴责选择了开枪自杀。同时斯维德里加伊洛夫的自杀为拉斯克尔尼科夫指向了逃避这一条路，只是弥补了自己的“罪”，而没有受到对应的“罚”。</p><p>与斯维德里加伊洛夫不同的是，拉斯克尔尼科夫有爱他妈妈妹妹，有爱他的挚友，还有爱他的索尼娅。这些爱着拉斯克尔尼科夫的人，都迫使拉斯克尔尼科夫良心受到越来越大的谴责，越来越拟人。在听闻德里加伊洛夫的自杀的消息后，拉斯克尔尼科夫第一时间也是想到了自杀去逃避自己的罪，正是因为索尼娅的爱，让拉斯克尔尼科夫选择自首来弥补自己犯下的罪。</p><p>本书颇具讽刺的结尾，描写了拉斯克尔尼科夫做过的一系列善事，比如从燃烧的建筑物中救出了孩子，不止一次的将最后一点钱送给别人。这和他的罪行形成了鲜明对比，倘若拉斯克尔尼科夫的理论是正确的，那么他的善行足以弥补他内心的后悔与罪恶感。相反，拉斯克尔尼科夫在西伯利亚的头几个月里，同样看不起其他罪犯，疾病也愈发严重。恶绝不是正当的，妄图以任何理论都无法平息内疚的良心。</p><p>另外颇为玩味的一点是拉斯克尔尼科夫至始至终都是一个无神论者，然而最后却因为内心实在痛苦，选择从索尼娅那里借来福音书，借由宗教的信仰抚平自己内心的痛苦。我不知道陀思妥耶夫斯基是否信仰东正教，至少从文中能看出他对无神论者的讽刺。其中有一段拉斯克尔尼科夫让索尼娅阅读拉撒路复活的情节，和拉斯克尔尼科夫的经历相对应。拉撒路病死后借由耶稣之手复活，拉斯克尔尼科夫在痛苦万分的时候借由上帝寻求慰藉，无疑能够看出陀思妥耶夫斯基对宗教的认可。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jin-xian-shi-mou-ge-xing-zheng-qu-hua-cesium-wa-kong/</id>
      <title type="text">仅显示某个行政区划（cesium 挖孔）</title>
      <published>2025-11-25T23:14:39.000Z</published>
      <updated>2025-11-25T23:14:39.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jin-xian-shi-mou-ge-xing-zheng-qu-hua-cesium-wa-kong/"/>
      <summary type="text">添加polygon, 中间挖孔, 仅显示某个区域 GeoJSON为 GeoJSON , 行政区划下载</summary>
      <content type="html"><![CDATA[<p>添加polygon, 中间挖孔, 仅显示某个区域</p>
<p>GeoJSON为 GeoJSON , <a href="https://datav.aliyun.com/portal/school/atlas/area_selector" target="_blank">行政区划下载</a></p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>// 3.1. 定义一个矩形作为遮罩的“外环”, 大致覆盖整个中国</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 格式：[lng, lat, lng, lat, ...]</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>worldRectangle</span><span> </span><span>=</span><span> [</span><span>73.5</span><span>, </span><span>4.5</span><span>, </span><span>73.5</span><span>, </span><span>53.5</span><span>, </span><span>135.5</span><span>, </span><span>53.5</span><span>, </span><span>135.5</span><span>, </span><span>4.5</span><span>]</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>// 3.2. 从 lt.json 中提取理塘县的边界作为“孔洞”</span></div></div><div><div><div>6</div></div><div><span>  </span><span>// lt.json 是一个 FeatureCollection，我们获取第一个 feature</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> </span><span>litangFeature</span><span> </span><span>=</span><span><span> (</span><span>GeoJSON</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>features</span><span>[</span><span>0</span><span>]</span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>litangPolygons</span><span> </span><span>=</span><span><span> </span><span>litangFeature</span><span>.</span></span><span>geometry</span><span>.</span><span>coordinates</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>litangPolygons</span><span>)</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>// 3.3. 创建遮罩多边形的层级</span></div></div><div><div><div>11</div></div><div><span>  </span><span>// Cesium 的 PolygonHierarchy 需要一个外环和一组内环（孔洞）</span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> </span><span>holes</span><span>:</span><span> </span><span>Cesium</span><span>.</span><span>PolygonHierarchy</span><span>[] </span><span>=</span><span> []</span></div></div><div><div><div>13</div></div><div><span>  </span><span>let</span><span><span> </span><span>holeCoords</span></span><span>:</span><span> </span><span>number</span><span>[] </span><span>=</span><span> []</span></div></div><div><div><div>14</div></div><div><span>  </span><span>// lt.json 是 MultiPolygon，所以需要遍历它包含的每一个 Polygon</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>litangPolygons</span><span>.</span><span>forEach</span><span><span>((</span><span>polygon</span></span><span>:</span><span> </span><span>number</span><span>[][][]) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>// 每个 Polygon 又是环的数组（第一个是外环，后面是内环）</span></div></div><div><div><div>17</div></div><div><span>    </span><span>// 在我们的遮罩中，理塘的*所有*环都是孔洞</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>polygon</span><span>.</span><span>forEach</span><span><span>((</span><span>ring</span></span><span>:</span><span> </span><span>number</span><span>[][]) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>// Cesium.Cartesian3.fromDegreesArray 需要一个扁平的 [lng, lat, lng, lat, ...] 数组</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>holeCoords</span><span> </span><span>=</span><span><span> </span><span>ring</span><span>.</span></span><span>flat</span><span>()</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>holes</span><span>.</span><span>push</span><span>(</span><span>new</span><span><span> </span><span>Cesium</span><span>.</span></span><span>PolygonHierarchy</span><span><span>(</span><span>Cesium</span><span>.</span></span><span>Cartesian3</span><span>.</span><span>fromDegreesArray</span><span><span>(</span><span>holeCoords</span><span>)))</span></span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>})</span></div></div><div><div><div>24</div></div><div><span>  </span><span>// 3.4. 创建最终的遮罩层级</span></div></div><div><div><div>25</div></div><div><span>  </span><span>const</span><span> </span><span>maskHierarchy</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>Cesium</span><span>.</span></span><span>PolygonHierarchy</span><span>(</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>Cesium</span><span>.</span><span>Cartesian3</span><span>.</span><span>fromDegreesArray</span><span><span>(</span><span>worldRectangle</span><span>), </span></span><span>// 全球外环</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>holes</span><span> </span><span>// 理塘县孔洞</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>)</span></div></div><div><div><div>29</div></div><div><span>  </span><span>// 3.5. 将遮罩添加到地球上</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>entities</span><span>.</span><span>add</span><span>({</span></div></div><div><div><div>31</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Litang Mask'</span><span>,</span></div></div><div><div><div>32</div></div><div><span>    </span><span>polygon</span><span>:</span><span> {</span></div></div><div><div><div>33</div></div><div><span>      </span><span>hierarchy</span><span>:</span><span><span> </span><span>maskHierarchy</span><span>,</span></span></div></div><div><div><div>34</div></div><div><span>      </span><span>perPositionHeight</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>35</div></div><div><span>      </span><span>// material: Cesium.Color.fromCssColorString('#071B32'),</span></div></div><div><div><div>36</div></div><div><span>      </span><span>material</span><span>:</span><span> </span><span>'/bg.png'</span><span>,</span></div></div><div><div><div>37</div></div><div><span>      </span><span>// material: 'https://p8.itc.cn/images01/20200723/b48a6c775c944f7d89043ab8e0154197.jpeg',</span></div></div><div><div><div>38</div></div><div><span>      </span><span>// fill: true, // 默认</span></div></div><div><div><div>39</div></div><div><span>      </span><span>// outline: false, // 默认</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<blockquote><p><code>Array.prototype.flat()</code> <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat" target="_blank">flat()</a> 创建一个新的数组，并根据指定深度递归地将所有子数组元素拼接到新的数组中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>arr2</span><span> </span><span>=</span><span> [</span><span>0</span><span>, </span><span>1</span><span>, [</span><span>2</span><span>, [</span><span>3</span><span>, [</span><span>4</span><span>, </span><span>5</span><span>]]]];</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>arr2</span><span>.</span></span><span>flat</span><span>());</span></div></div><div><div><div>4</div></div><div><span>// expected output: Array [0, 1, 2, Array [3, Array [4, 5]]]</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>arr2</span><span>.</span></span><span>flat</span><span>(</span><span>2</span><span>));</span></div></div><div><div><div>7</div></div><div><span>// expected output: Array [0, 1, 2, 3, Array [4, 5]]</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>arr2</span><span>.</span></span><span>flat</span><span>(</span><span>Infinity</span><span>));</span></div></div><div><div><div>10</div></div><div><span>// expected output: Array [0, 1, 2, 3, 4, 5]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></blockquote>
<p>限制camera视角范围</p>
<p>遍历GeoJSON获取边界矩形, 返回 <code>new  Cesium.Rectangle(west, south, east, north)</code></p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>getLitangBoundingBox</span><span> </span><span>=</span><span><span> (</span><span>geoJson</span></span><span>:</span><span> </span><span>any</span><span>)</span><span>:</span><span> </span><span>Cesium</span><span>.</span><span>Rectangle</span><span> </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>minLng</span><span> </span></span><span>=</span><span> </span><span>180</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>minLat</span><span> </span><span>=</span><span> </span><span>90</span><span>,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>maxLng</span><span> </span><span>=</span><span> </span><span>-</span><span>180</span><span>,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>maxLat</span><span> </span><span>=</span><span> </span><span>-</span><span>90</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>// 遍历 lt.json 的坐标</span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>polygons</span><span> </span><span>=</span><span><span> </span><span>geoJson</span><span>.</span></span><span>features</span><span>[</span><span>0</span><span>].</span><span>geometry</span><span>.</span><span>coordinates</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>polygons</span><span>.</span><span>forEach</span><span><span>((</span><span>polygon</span></span><span>:</span><span> </span><span>number</span><span>[][][]) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>polygon</span><span>.</span><span>forEach</span><span><span>((</span><span>ring</span></span><span>:</span><span> </span><span>number</span><span>[][]) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>ring</span><span>.</span><span>forEach</span><span><span>((</span><span>point</span></span><span>:</span><span> </span><span>number</span><span>[]) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>const</span><span> </span><span>lng</span><span> </span><span>=</span><span><span> </span><span>point</span><span>[</span></span><span>0</span><span>]</span></div></div><div><div><div>13</div></div><div><span>        </span><span>const</span><span> </span><span>lat</span><span> </span><span>=</span><span><span> </span><span>point</span><span>[</span></span><span>1</span><span>]</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>minLng</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>minLng</span><span>, </span><span>lng</span><span>)</span></span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>maxLng</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>max</span><span><span>(</span><span>maxLng</span><span>, </span><span>lng</span><span>)</span></span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>minLat</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>minLat</span><span>, </span><span>lat</span><span>)</span></span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>maxLat</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>max</span><span><span>(</span><span>maxLat</span><span>, </span><span>lat</span><span>)</span></span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>})</span></div></div><div><div><div>21</div></div><div><span>  </span><span>return</span><span><span> </span><span>Cesium</span><span>.</span></span><span>Rectangle</span><span>.</span><span>fromDegrees</span><span><span>(</span><span>minLng</span><span>, </span><span>minLat</span><span>, </span><span>maxLng</span><span>, </span><span>maxLat</span><span>)</span></span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<p>初始化时, camera定位到该区域</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>const</span><span> </span><span>MAX_ALTITUDE</span><span> </span><span>=</span><span> </span><span>500000</span><span> </span><span>// 500km (最远)</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>MIN_ALTITUDE</span><span> </span><span>=</span><span> </span><span>1000</span><span> </span><span>// 1km (最近)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>// 将地球背景和天空盒设置为透明</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>scene</span><span>.</span><span>backgroundColor</span><span> </span><span>=</span><span><span> </span><span>Cesium</span><span>.</span></span><span>Color</span><span>.</span><span>TRANSPARENT</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>scene</span><span>.</span><span>globe</span><span>.</span><span>baseColor</span><span> </span><span>=</span><span><span> </span><span>Cesium</span><span>.</span></span><span>Color</span><span>.</span><span>TRANSPARENT</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>_cesiumWidget</span><span>.</span><span>_creditContainer</span><span>.</span><span>style</span><span>.</span><span>display</span><span> </span><span>=</span><span> </span><span>'none'</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>scene</span><span>.</span><span>globe</span><span>.</span><span>depthTestAgainstTerrain</span><span> </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>  </span><span>// --- 设置相机缩放和倾斜限制 ---</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>scene</span><span>.</span><span>screenSpaceCameraController</span><span>.</span><span>minimumZoomDistance</span><span> </span><span>=</span><span> </span><span>MIN_ALTITUDE</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>scene</span><span>.</span><span>screenSpaceCameraController</span><span>.</span><span>maximumZoomDistance</span><span> </span><span>=</span><span> </span><span>MAX_ALTITUDE</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>scene</span><span>.</span><span>screenSpaceCameraController</span><span>.</span><span>enableTilt</span><span> </span><span>=</span><span> </span><span>false</span><span> </span><span>// 2D 模式下禁止倾斜</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>//  设置camera</span></div></div><div><div><div>17</div></div><div><span>  </span><span>const</span><span> </span><span>litangBoundsDeg</span><span> </span><span>=</span><span> </span><span>getLitangBoundingBox</span><span><span>(</span><span>GeoJSON</span><span>)</span></span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>viewer</span><span>.</span><span>camera</span><span>.</span><span>setView</span><span>({</span></div></div><div><div><div>19</div></div><div><span>    </span><span>destination</span><span>:</span><span><span> </span><span>litangBoundsDeg</span><span>,</span></span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/di-yi-ge-cesium-xiang-mu/</id>
      <title type="text">第一个cesium项目</title>
      <published>2025-10-08T01:14:08.000Z</published>
      <updated>2025-10-08T01:14:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/di-yi-ge-cesium-xiang-mu/"/>
      <summary type="text">这里使用vite搭建demo, 选择 Vanilla （代表原生 JS）</summary>
      <content type="html"><![CDATA[<section><h2>第一个cesium项目<a href="#第一个cesium项目"><span>#</span></a></h2><section><h3>创建 Vite 项目<a href="#创建-vite-项目"><span>#</span></a></h3><p>这里使用vite搭建demo, <strong>选择 <code>Vanilla</code></strong> （代表原生 JS）</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>create</span><span> </span><span>vite@latest</span><span> </span><span>my-cesium-demo</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>安装 Cesium<a href="#安装-cesium"><span>#</span></a></h3><p>我们需要安装 Cesium 库本身，以及一个专门为 Vite 打造的 Cesium 插件。</p><ol>
<li>
<p>安装 Cesium:</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>cesium</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>安装 Vite 插件 (vite-plugin-cesium): 这个插件会自动帮你处理 Cesium 的静态资源。</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>vite-plugin-cesium</span><span> </span><span>-D</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section><section><h3>配置 Vite<a href="#配置-vite"><span>#</span></a></h3><p>在项目根目录新建 <code>vite.config.js</code> 文件。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>defineConfig</span><span> } </span><span>from</span><span> </span><span>'vite'</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>cesium</span><span> </span><span>from</span><span> </span><span>'vite-plugin-cesium'</span><span>; </span><span>// 引入插件</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>defineConfig</span><span>({</span></div></div><div><div><div>5</div></div><div><span>  </span><span>plugins</span><span>:</span><span> [</span></div></div><div><div><div>6</div></div><div><span>    </span><span>cesium</span><span>() </span><span>// 使用插件</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>8</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Cesium 代码<a href="#cesium-代码"><span>#</span></a></h3><p>打开 <code>index.html</code>，把 <code>&lt;body&gt;</code> 里的内容改成一个简单的 <code>div</code>，作为 Cesium 的容器：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!</span><span>doctype</span><span> </span><span>html</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>html</span><span> </span><span>lang</span><span>=</span><span>"en"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;</span><span>head</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>meta</span><span> </span><span>charset</span><span>=</span><span>"UTF-8"</span><span> /&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>link</span><span> </span><span>rel</span><span>=</span><span>"icon"</span><span> </span><span>type</span><span>=</span><span>"image/svg+xml"</span><span> </span><span>href</span><span>=</span><span>"/vite.svg"</span><span> /&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;</span><span>meta</span><span> </span><span>name</span><span>=</span><span>"viewport"</span><span> </span><span>content</span><span>=</span><span>"width=device-width, initial-scale=1.0"</span><span> /&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>title</span><span>&gt;Cesium Demo&lt;/</span><span>title</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;/</span><span>head</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&lt;</span><span>body</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"cesiumContainer"</span><span>&gt;&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>&lt;</span><span>script</span><span> </span><span>type</span><span>=</span><span>"module"</span><span> </span><span>src</span><span>=</span><span>"/main.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>&lt;/</span><span>body</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span>&lt;/</span><span>html</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>main.js</p><div><div><div><figure><figcaption><span>main.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 引入 Cesium 的 CSS</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>'cesium/Build/Cesium/Widgets/widgets.css'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 2. 引入 Cesium 库</span></div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>Cesium</span><span> </span><span>from</span><span> </span><span>'cesium'</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 3. 设置 Cesium 静态资源路径</span></div></div><div><div><div>8</div></div><div><span>// (这一步很重要，vite-plugin-cesium 会自动处理，但我们需要告诉 Cesium 从哪里加载)</span></div></div><div><div><div>9</div></div><div><span><span>window</span><span>.</span></span><span>CESIUM_BASE_URL</span><span> </span><span>=</span><span> </span><span>'/Cesium/'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 4. (可选) 设置你的 Cesium Ion Token</span></div></div><div><div><div>12</div></div><div><span>const</span><span> </span><span>ACCESS_TOKEN</span><span> </span><span>=</span></div></div><div><div><div>13</div></div><div><span>  </span><span>'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4ZjRiOWJjYS05YjFlLTQwMDItODJlMy1iNzNkODU5NDJiMjIiLCJpZCI6Mjg1NjUyLCJpYXQiOjE3NDIzNjYzODB9._KJdWwzj8NIjoVImtrNdClZ3zqa44wo3qb5cOXdOpBU'</span></div></div><div><div><div>14</div></div><div><span><span>Cesium</span><span>.</span></span><span>Ion</span><span>.</span><span>defaultAccessToken</span><span> </span><span>=</span><span> </span><span>ACCESS_TOKEN</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 5. 初始化 Cesium Viewer</span></div></div><div><div><div>17</div></div><div><span>const</span><span> </span><span>viewer</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>Cesium</span><span>.</span></span><span>Viewer</span><span>(</span><span>'cesiumContainer'</span><span>, {</span></div></div><div><div><div>18</div></div><div><span>  </span><span>// --- 为了保持 Demo 简洁，我们关闭一些默认控件 ---</span></div></div><div><div><div>19</div></div><div><span>  </span><span>animation</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示动画控件</span></div></div><div><div><div>20</div></div><div><span>  </span><span>baseLayerPicker</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示图层选择器</span></div></div><div><div><div>21</div></div><div><span>  </span><span>fullscreenButton</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示全屏按钮</span></div></div><div><div><div>22</div></div><div><span>  </span><span>geocoder</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示地名查找控件</span></div></div><div><div><div>23</div></div><div><span>  </span><span>homeButton</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示 Home 按钮</span></div></div><div><div><div>24</div></div><div><span>  </span><span>infoBox</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示信息框</span></div></div><div><div><div>25</div></div><div><span>  </span><span>sceneModePicker</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示3D/2D/哥伦布视图切换器</span></div></div><div><div><div>26</div></div><div><span>  </span><span>selectionIndicator</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示选取指示器</span></div></div><div><div><div>27</div></div><div><span>  </span><span>timeline</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示时间线</span></div></div><div><div><div>28</div></div><div><span>  </span><span>navigationHelpButton</span><span>:</span><span> </span><span>false</span><span>, </span><span>// 是否显示导航帮助按钮</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>})</span></div></div><div><div><div>31</div></div><div><span>// (可选) 关掉 logo</span></div></div><div><div><div>32</div></div><div><span><span>viewer</span><span>.</span></span><span>_cesiumWidget</span><span>.</span><span>_creditContainer</span><span>.</span><span>style</span><span>.</span><span>display</span><span> </span><span>=</span><span> </span><span>'none'</span></div></div><div><div><div>33</div></div><div><span>// 移除原有的layer</span></div></div><div><div><div>34</div></div><div><span><span>viewer</span><span>.</span></span><span>imageryLayers</span><span>.</span><span>remove</span><span><span>(</span><span>viewer</span><span>.</span></span><span>imageryLayers</span><span>.</span><span>get</span><span>(</span><span>0</span><span>))</span></div></div><div><div><div>35</div></div><div><span>// viewer.imageryLayers.removeAll()</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>// 添加一个在 Ion 注册的 layer</span></div></div><div><div><div>38</div></div><div><span>const</span><span> </span><span>imageryLayer</span><span> </span><span>=</span><span><span> </span><span>Cesium</span><span>.</span></span><span>ImageryLayer</span><span>.</span><span>fromProviderAsync</span><span><span>(</span><span>Cesium</span><span>.</span></span><span>IonImageryProvider</span><span>.</span><span>fromAssetId</span><span>(</span><span>3954</span><span>))</span></div></div><div><div><div>39</div></div><div><span><span>viewer</span><span>.</span></span><span>imageryLayers</span><span>.</span><span>add</span><span><span>(</span><span>imageryLayer</span><span>)</span></span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'Cesium + Vite demo is running!'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/shi-yong-zhe-se-qi-hui-zhi-bo-lang/</id>
      <title type="text">使用着色器绘制波浪</title>
      <published>2025-05-17T03:05:53.000Z</published>
      <updated>2025-05-17T03:05:53.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/shi-yong-zhe-se-qi-hui-zhi-bo-lang/"/>
      <summary type="text">通过 perlin noise 增加在主浪身上的小波浪 循环调用 perlin noise 显得更加无序 但是多次调用 perlin noise，会导致小波浪的频率变小，但我们希望和主浪保持一样的高度，所以乘以 i 扩大频率，除以 i 缩小高度</summary>
      <content type="html"><![CDATA[<p>通过 perlin noise 增加在主浪身上的小波浪
循环调用 perlin noise 显得更加无序
但是多次调用 perlin noise，会导致小波浪的频率变小，但我们希望和主浪保持一样的高度，所以乘以 i 扩大频率，除以 i 缩小高度</p>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/shi-yong-zhe-se-qi-hui-zhi-tu-an/</id>
      <title type="text">使用着色器绘制图案</title>
      <published>2025-05-07T01:15:53.000Z</published>
      <updated>2025-05-07T01:15:53.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/shi-yong-zhe-se-qi-hui-zhi-tu-an/"/>
      <summary type="text">使用 glsl 语法创建常见的图案以及颜色混合</summary>
      <content type="html"><![CDATA[<section><h2>What is UV<a href="#what-is-uv"><span>#</span></a></h2><p>在创建着色器时，我们需要绘制特定的图案，如星星、圆圈、光透镜、波浪等。虽然可以使用纹理，但绘制形状可以让我们有更多的控制权;我们可以对形状参数进行动画处理，并且没有要加载的纹理。需要在fragment shader中用到uv坐标。</p><p>什么是uv坐标？</p><p>UV坐标是用来将2D纹理贴图到3D模型表面的坐标系。这里的U和V代表的是2D空间的两个轴，类似于平面中的X和Y，但为了避免与3D空间中的X、Y、Z混淆，所以用U和V来命名。</p><p>在Threejs中UV坐标的范围是0到1，覆盖整个纹理图像。原点(0,0)在左下角，而(1,1)在右上角。uv坐标的count取决于material的分段数</p><p>如何将uv坐标发送到fragment shader？</p><p>fragment shader无法直接访问顶点数据，所以我们需要通过 vertex shader 创建<code>vUv</code>变量发送给 fragment shader。</p><div><div><div><figure><figcaption><span>vertex.glsl</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>gl_Position </span><span>=</span><span> projectionMatrix </span><span>*</span><span> modelViewMatrix </span><span>*</span><span> </span><span>vec4</span><span>(position, </span><span>1.0</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>vUv </span><span>=</span><span> uv;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// fragment.glsl</span></div></div><div><div><div>10</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>13</div></div><div><span>{</span></div></div><div><div><div>14</div></div><div><span>    </span><span>// use vUv</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(vUv, </span><span>1.0</span><span>, </span><span>1.0</span><span>)</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>Pattern 1<a href="#pattern-1"><span>#</span></a></h2><p>直接使用 uv 坐标，原点的颜色为<code>(0.0, 0.0, 1.0, 1.0)</code>到右上角<code>(1.0, 1.0, 1.0, 1.0)</code>的渐变</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(vUv, </span><span>1.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422155105545_1745308268.png" alt="image-20250422155105545" /><figcaption>image-20250422155105545</figcaption></figure><p></p></section>
<section><h2>Pattern 2<a href="#pattern-2"><span>#</span></a></h2><p>同样的使用 uv 坐标，但<code>b</code>设置为<code>0.0</code>，为<code>(0.0, 0.0, 0.0, 1.0)</code>到右上角<code>(1.0, 1.0, 0.0, 1.0)</code>的渐变</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(vUv, </span><span>0.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422155853192_1745308737.png" alt="image-20250422155853192" /><figcaption>image-20250422155853192</figcaption></figure><p></p></section>
<section><h2>Pattern 3<a href="#pattern-3"><span>#</span></a></h2><p>只使用 uv 坐标的<code>x</code>值，得到一个在x轴上的黑白渐变。颜色在x轴上y轴上每个点都是<code>(0.0, 0.0, 0.0, 1.0)</code>到<code>(1.0, 1.0, 1.0, 1.0)</code>变化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>vUv</span><span>.</span><span>x</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422160614995_1745309476.png" alt="image-20250422160614995" /><figcaption>image-20250422160614995</figcaption></figure><p></p></section>
<section><h2>Pattern 4<a href="#pattern-4"><span>#</span></a></h2><p>和 pattern 3 完全相同，不过应用在y轴上。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>vUv</span><span>.</span><span>y</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422160752938_1745309468.png" alt="image-20250422160752938" /><figcaption>image-20250422160752938</figcaption></figure><p></p></section>
<section><h2>Pattern 5 (1.0 - …)<a href="#pattern-5-10---"><span>#</span></a></h2><p>将 pattern 4 的值通过 <code>1.0 - ...</code>反转。<code>(1.0, 1.0, 1.0, 1.0)</code> 到<code>(0.0, 0.0, 0.0, 1.0)</code>变化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>vUv</span><span>.</span><span>y</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422161048251_1745309451.png" alt="image-20250422161048251" /><figcaption>image-20250422161048251</figcaption></figure><p></p></section>
<section><h2>Pattern 6<a href="#pattern-6"><span>#</span></a></h2><p>将 uv.y 乘以 10，<code>gl_FragColor</code>将很快的到达<code>(1.0, 1.0, 1.0, 1.0)</code>，后面大于<code>1.0</code>也显示白色</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>vUv</span><span>.</span><span>x</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422161448435_1745309689.png" alt="image-20250422161448435" /><figcaption>image-20250422161448435</figcaption></figure><p></p></section>
<section><h2>Pattern 7 mod(…)<a href="#pattern-7-mod"><span>#</span></a></h2><p>通过取模运算 <code>mod(...)</code>，重复某一段的变化。</p><p>将y的值乘以10.0，模1.0，分为10段，每段都是<code>(0.0, 0.0, 0.0, 1.0)</code>到<code>(1.0, 1.0, 1.0, 1.0)</code>变化。如果模0.5，最大值为0.5，结果就是<code>(0.0, 0.0, 0.0, 1.0)</code>到<code>(0.5, 0.5, 0.5, 1.0)</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>mod</span><span>(</span><span>vUv</span><span>.</span><span>y</span><span><span> </span><span>*</span><span> </span></span><span>10.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422162044088_1745310045.png" alt="image-20250422162044088" /><figcaption>image-20250422162044088</figcaption></figure><p></p></section>
<section><h2>Pattern 8 step(…)<a href="#pattern-8-step"><span>#</span></a></h2><p>在 pattern 7 的基础上实现斑马条纹，如果颜色在0.5以下，设为0.0；在0.5以上，设置1.0。</p><p>可以通过<code>if()</code>判断实现，但是会影响性能。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>mod</span><span>(</span><span>vUv</span><span>.</span><span>y</span><span><span> </span><span>*</span><span> </span></span><span>10.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span><span>(strength </span><span>&lt;</span><span> </span></span><span>0.5</span><span>) {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>strength </span><span>=</span><span> </span><span>0.0</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>strength </span><span>=</span><span> </span><span>1.0</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// or</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>strength </span><span>=</span><span> strength </span><span>&lt;</span><span> </span><span>0.5</span><span><span> </span><span>?</span><span> </span></span><span>0.0</span><span><span> </span><span>:</span><span> </span></span><span>1.0</span><span>;</span><span> // 三元运算符</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>glsl 提供了<code>step(edge, x)</code>函数。</p><ul>
<li><strong>参数</strong>：<code>edge</code> 是阈值，<code>x</code> 是输入值。</li>
<li><strong>返回值</strong>：若 <code>x &gt;= edge</code> 返回 <code>1.0</code>，否则返回 <code>0.0</code>。</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>mod</span><span>(</span><span>vUv</span><span>.</span><span>y</span><span><span> </span><span>*</span><span> </span></span><span>10.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>strength </span><span>=</span><span> </span><span>step</span><span>(</span><span>0.5</span><span>, strength);</span><span> // 超过阈值0.5，返回1.0，否则返回0.0</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(</span><span>vec3</span><span>(strength), </span><span>1.0</span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422163923388_1745311164.png" alt="image-20250422163923388" /><figcaption>image-20250422163923388</figcaption></figure><p></p></section>
<section><h2>Pattern 9<a href="#pattern-9"><span>#</span></a></h2><p>通过调整 <code>step()</code>的阈值，更改条纹的粗细。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>mod</span><span><span>(vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, strength);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422164320581_1745311401.png" alt="image-20250422164320581" /><figcaption>image-20250422164320581</figcaption></figure><p></p></section>
<section><h2>Pattern 10<a href="#pattern-10"><span>#</span></a></h2><p>将 pattern 9 中修改<code>vUv.x</code>，在x轴上分段(竖条纹)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422164543169_1745311544.png" alt="image-20250422164543169" /><figcaption>image-20250422164543169</figcaption></figure><p></p></section>
<section><h2>Pattern 11 +<a href="#pattern-11-"><span>#</span></a></h2><p>将 pattern 9 和 pattern 10 的图案叠加，可以使用<code>+</code>取并集</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>+=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422164920441_1745311762.png" alt="image-20250422164920441" /><figcaption>image-20250422164920441</figcaption></figure><p></p></section>
<section><h2>Pattern 12 *<a href="#pattern-12-"><span>#</span></a></h2><p>将 pattern 9 和 pattern 10 的图案取相交处，可以使用<code>*</code>取交集</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>*=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span><span> // 只能看到交点处</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422165050274_1745311851.png" alt="image-20250422165050274" /><figcaption>image-20250422165050274</figcaption></figure><p></p></section>
<section><h2>Pattern 13<a href="#pattern-13"><span>#</span></a></h2><p>在 pattern 12 上，将x轴上的分段阈值缩小，x轴方向上条纹更粗。然后取交集。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.4</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>*=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422165421888_1745312063.png" alt="image-20250422165421888" /><figcaption>image-20250422165421888</figcaption></figure><p></p></section>
<section><h2>Pattern 14<a href="#pattern-14"><span>#</span></a></h2><p>将 pattern 13 的图案分别按照水平方向(barX)和竖直方向(barY)实现，然后再取并集。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> barX </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.4</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>barX </span><span>*=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>3</div></div><div><span>float</span><span><span> barY </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.4</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>4</div></div><div><span><span>barY </span><span>*=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>5</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> barX </span><span>+</span><span> barY;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422170934207_1745312977.png" alt="image-20250422170934207" /><figcaption>image-20250422170934207</figcaption></figure><p></p></section>
<section><h2>Pattern 15<a href="#pattern-15"><span>#</span></a></h2><p>基于 pattern 14 将 barX 和 barY 分别沿各自的方向偏移0.2，得到 + 图案</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> barX </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.4</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>barX </span><span>*=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span> </span><span>+</span><span> </span></span><span>0.2</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>3</div></div><div><span>float</span><span><span> barY </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.4</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>4</div></div><div><span><span>barY </span><span>*=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span> </span><span>+</span><span> </span></span><span>0.2</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>5</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> barX </span><span>+</span><span> barY;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250422171220785_1745313141.png" alt="image-20250422171220785" /><figcaption>image-20250422171220785</figcaption></figure><p></p></section>
<section><h2>Pattern 16 abs()<a href="#pattern-16-abs"><span>#</span></a></h2><p>绘制x轴方向，0.5到0.0到0.5的渐变。<code>abs()</code>取绝对值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>abs</span><span><span>(vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423134807735_1745387289.png" alt="image-20250423134807735" /><figcaption>image-20250423134807735</figcaption></figure><p></p></section>
<section><h2>Pattern 17 min()<a href="#pattern-17-min"><span>#</span></a></h2><p>在水平方向和竖直方向取图案的最小值<code>min()</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>min</span><span>(</span><span>abs</span><span><span>(</span><span>vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span>), </span><span>abs</span><span><span>(</span><span>vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423135138735_1745387499.png" alt="image-20250423135138735" /><figcaption>image-20250423135138735</figcaption></figure><p></p></section>
<section><h2>Pattern 18 max()<a href="#pattern-18-max"><span>#</span></a></h2><p>在水平方向和竖直方向取最大值<code>max()</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>max</span><span>(</span><span>abs</span><span><span>(</span><span>vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span>), </span><span>abs</span><span><span>(</span><span>vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423135305648_1745387586.png" alt="image-20250423135305648" /><figcaption>image-20250423135305648</figcaption></figure><p></p></section>
<section><h2>Pattern 19<a href="#pattern-19"><span>#</span></a></h2><p>基于 pattern 18 ，使用 <code>step()</code>分段显示，实现矩形环。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>max</span><span>(</span><span>abs</span><span><span>(</span><span>vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span>), </span><span>abs</span><span><span>(</span><span>vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.2</span><span>, strength);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423135630483_1745387792.png" alt="image-20250423135630483" /><figcaption>image-20250423135630483</figcaption></figure><p></p></section>
<section><h2>Pattern 20<a href="#pattern-20"><span>#</span></a></h2><p>基于 pattern 19，通过取反<code>(1.0 - ...)</code>操作实现矩形<code>square2</code>，再与<code>square1</code>相乘取交集，构成更细的矩形环。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> square1 </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.2</span><span>, </span><span>max</span><span>(</span><span>abs</span><span><span>(</span><span>vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span><span>),</span><span> </span></span><span>abs</span><span><span>(</span><span>vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>)));</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> square2 </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>step</span><span>(</span><span>0.25</span><span>, </span><span>max</span><span>(</span><span>abs</span><span><span>(</span><span>vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span><span>),</span><span> </span></span><span>abs</span><span><span>(</span><span>vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>)));</span></div></div><div><div><div>3</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> square1 </span><span>*</span><span> square2;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423140032193_1745388033.png" alt="image-20250423140032193" /><figcaption>image-20250423140032193</figcaption></figure><p></p></section>
<section><h2>Pattern 21 floor()<a href="#pattern-21-floor"><span>#</span></a></h2><p>水平方向的渐变条纹，<code>floor()</code>向下取整，分成x段就乘x除x。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>floor</span><span><span>(vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423140221139_1745388142.png" alt="image-20250423140221139" /><figcaption>image-20250423140221139</figcaption></figure><p></p></section>
<section><h2>Pattern 22<a href="#pattern-22"><span>#</span></a></h2><p>基于 pattern 21, 将水平方向和竖直方向取交集得到，对角线渐变的条纹</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>floor</span><span><span>(vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>*=</span><span> </span></span><span>floor</span><span><span>(vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423140455616_1745388297.png" alt="image-20250423140455616" /><figcaption>image-20250423140455616</figcaption></figure><p></p></section>
<section><h2>Pattern 23 random()<a href="#pattern-23-random"><span>#</span></a></h2><p>目的是每个 uv 坐标的颜色都取随机数。但是 glsl 中没有内置的随机数方法，只能通过自定义函数得到一个看起来随机的[0, 1]的数。下面的方法通过接收 <code>vec2</code>返回一个相对随机的值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span> </span><span>random</span><span><span>(</span><span>vec2 </span><span>st</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span> </span><span>fract</span><span>(</span><span>sin</span><span>(</span><span>dot</span><span>(</span><span>st</span><span>.</span><span>xy</span><span>, </span><span>vec2</span><span>(</span><span>12.9898</span><span>, </span><span>78.233</span><span><span>))) </span><span>*</span><span> </span></span><span>43758.5453123</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在<code>main()</code>中使用，形成雪花屏。因为<code>random()</code>是固定的，每次渲染都是相同的图案。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>random</span><span><span>(</span><span>vUv</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423141028588_1745388629.png" alt="image-20250423141028588" /><figcaption>image-20250423141028588</figcaption></figure><p></p></section>
<section><h2>Pattern 24<a href="#pattern-24"><span>#</span></a></h2><p>基于 pattern 22 和 23，每个区域的坐标都取随机值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> gridUv </span><span>=</span><span> </span></span><span>vec2</span><span>(</span><span>floor</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>, </span><span>floor</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>random</span><span><span>(</span><span>gridUv</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423151504622_1745392505.png" alt="image-20250423151504622" /><figcaption>image-20250423151504622</figcaption></figure><p></p></section>
<section><h2>Pattern 25<a href="#pattern-25"><span>#</span></a></h2><p>基于 Pattern 24, 给竖直方向设置x偏移</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> gridUv </span><span>=</span><span> </span></span><span>vec2</span><span>(</span><span>floor</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>, </span><span>floor</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span> </span><span>+</span><span> vUv.x </span><span>*</span><span> </span></span><span>5.0</span><span><span>) </span><span>/</span><span> </span></span><span>10.0</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>random</span><span><span>(</span><span>gridUv</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423154626184_1745394387.png" alt="image-20250423154626184" /><figcaption>image-20250423154626184</figcaption></figure><p></p></section>
<section><h2>Pattern 26 length()<a href="#pattern-26-length"><span>#</span></a></h2><p>根据uv坐标上该点的模长。原点的值 0.0 到右上角的 √2</p><span><span><span>∥a⃗∥=x2+y2{\displaystyle \left\|{\vec {a}}\right\|={\sqrt {x^{2}+y^{2}}}}</span><span><span><span></span><span><span><span>∥</span><span><span><span><span><span><span><span></span><span>a</span></span><span><span></span><span><span></span></span></span></span></span></span></span></span><span>∥</span></span><span></span><span>=</span><span></span><span><span><span><span><span><span><span></span><span><span><span>x</span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span></span></span></span></span></span><span></span><span>+</span><span></span><span><span>y</span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>length</span><span>(vUv);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423154905082_1745394546.png" alt="image-20250423154905082" /><figcaption>image-20250423154905082</figcaption></figure><p></p></section>
<section><h2>Pattern 27 distance()<a href="#pattern-27-distance"><span>#</span></a></h2><p>计算uv坐标中各点，到<code>(0.5, 0.5)</code>的距离，从<code>(0.5,0.5)</code>的 0.0 到顶点的 √0.5</p><span><span><span>∥a⃗∥=(x−0.5)2+(y−0.5)2{\displaystyle \left\|{\vec {a}}\right\|={\sqrt {\left(x-0.5\right)^{2}+\left(y-0.5\right)^{2}}}}</span><span><span><span></span><span><span><span>∥</span><span><span><span><span><span><span><span></span><span>a</span></span><span><span></span><span><span></span></span></span></span></span></span></span></span><span>∥</span></span><span></span><span>=</span><span></span><span><span><span><span><span><span><span></span><span><span><span><span>(</span><span>x</span><span></span><span>−</span><span></span><span>0.5</span><span>)</span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span></span></span></span></span></span><span></span><span>+</span><span></span><span><span><span>(</span><span>y</span><span></span><span>−</span><span></span><span>0.5</span><span>)</span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>distance</span><span>(vUv, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>等价于向量减去0.5的模长。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>length</span><span><span>(vUv </span><span>-</span><span> </span></span><span>0.5</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423160137579_1745395298.png" alt="image-20250423160137579" /><figcaption>image-20250423160137579</figcaption></figure><p></p></section>
<section><h2>Pattern 28<a href="#pattern-28"><span>#</span></a></h2><p>基于 pattern 27 取反</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>distance</span><span>(vUv, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423160229323_1745395350.png" alt="image-20250423160229323" /><figcaption>image-20250423160229323</figcaption></figure><p></p></section>
<section><h2>Pattern 29<a href="#pattern-29"><span>#</span></a></h2><p>基于 pattern 27，坐标距离<code>(0.5, 0.5)</code>越近，<code>strength</code>的值越大。可以做星星效果，更改分子的值。但是平面的边缘不等于0，可能在特定的角度可以看到平面</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>0.015</span><span><span> </span><span>/</span><span> </span></span><span>distance</span><span>(vUv, </span><span>vec2</span><span>(</span><span>0.5</span><span><span>)) </span><span>+</span><span> </span></span><span>0.1</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423160502397_1745395503.png" alt="image-20250423160502397" /><figcaption>image-20250423160502397</figcaption></figure><p></p></section>
<section><h2>Pattern 30<a href="#pattern-30"><span>#</span></a></h2><p>在 pattern 29 上，变换vu坐标</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> lightUv </span><span>=</span><span> </span></span><span>vec2</span><span><span>(vUv.x </span><span>*</span><span> </span></span><span>0.1</span><span><span> </span><span>+</span><span> </span></span><span>0.45</span><span><span>, vUv.y </span><span>*</span><span> </span></span><span>0.5</span><span><span> </span><span>+</span><span> </span></span><span>0.25</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>0.015</span><span><span> </span><span>/</span><span> </span></span><span>distance</span><span>(lightUv, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423161053035_1745395853.png" alt="image-20250423161053035" /><figcaption>image-20250423161053035</figcaption></figure><p></p></section>
<section><h2>Pattern 31<a href="#pattern-31"><span>#</span></a></h2><p>将 pattern 30 的水平方向和竖直方向取交集，构成十字</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> lightUvX </span><span>=</span><span> </span></span><span>vec2</span><span><span>(vUv.x </span><span>*</span><span> </span></span><span>0.1</span><span><span> </span><span>+</span><span> </span></span><span>0.45</span><span><span>, vUv.y </span><span>*</span><span> </span></span><span>0.5</span><span><span> </span><span>+</span><span> </span></span><span>0.25</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> lightX </span><span>=</span><span> </span></span><span>0.015</span><span><span> </span><span>/</span><span> </span></span><span>distance</span><span>(lightUvX, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div><div><div><div>3</div></div><div><span>vec2</span><span><span> lightUvY </span><span>=</span><span> </span></span><span>vec2</span><span><span>(vUv.y </span><span>*</span><span> </span></span><span>0.1</span><span><span> </span><span>+</span><span> </span></span><span>0.45</span><span><span>, vUv.x </span><span>*</span><span> </span></span><span>0.5</span><span><span> </span><span>+</span><span> </span></span><span>0.25</span><span>);</span></div></div><div><div><div>4</div></div><div><span>float</span><span><span> lightY </span><span>=</span><span> </span></span><span>0.015</span><span><span> </span><span>/</span><span> </span></span><span>distance</span><span>(lightUvY, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div><div><div><div>5</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> lightX </span><span>*</span><span> lightY;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423161242287_1745395963.png" alt="image-20250423161242287" /><figcaption>image-20250423161242287</figcaption></figure><p></p></section>
<section><h2>Pattern 32 rotate()<a href="#pattern-32-rotate"><span>#</span></a></h2><p>将 pattern 31 旋转。</p><p>glsl 中没有内置旋转的函数，通过自定义<code>rotate()</code>实现</p><p>参数： uv：uv坐标，rotation：旋转角度，mid：旋转中心点</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>PI</span><span> </span><span>3.1415926535897932384626433832795</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>vec2</span><span> </span><span>rotate</span><span><span>(</span><span>vec2 </span><span>uv</span><span>,</span><span> </span></span><span>float</span><span><span> </span><span>rotation</span><span>,</span><span> vec2 </span><span>mid</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span> </span><span>vec2</span><span>(</span></div></div><div><div><div>5</div></div><div><span>        </span><span>cos</span><span><span>(rotation) </span><span>*</span><span> (</span></span><span>uv</span><span>.</span><span>x</span><span><span> </span><span>-</span><span> </span></span><span>mid</span><span>.</span><span>x</span><span><span>) </span><span>+</span><span> </span></span><span>sin</span><span><span>(rotation) </span><span>*</span><span> (</span></span><span>uv</span><span>.</span><span>y</span><span><span> </span><span>-</span><span> </span></span><span>mid</span><span>.</span><span>y</span><span><span>) </span><span>+</span><span> </span></span><span>mid</span><span>.</span><span>x</span><span>,</span></div></div><div><div><div>6</div></div><div><span>        </span><span>cos</span><span><span>(rotation) </span><span>*</span><span> (</span></span><span>uv</span><span>.</span><span>y</span><span><span> </span><span>-</span><span> </span></span><span>mid</span><span>.</span><span>y</span><span><span>) </span><span>-</span><span> </span></span><span>sin</span><span><span>(rotation) </span><span>*</span><span> (</span></span><span>uv</span><span>.</span><span>x</span><span><span> </span><span>-</span><span> </span></span><span>mid</span><span>.</span><span>x</span><span><span>) </span><span>+</span><span> </span></span><span>mid</span><span>.</span><span>y</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>main()</code>旋转45°</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> rotatedUv </span><span>=</span><span> </span></span><span>rotate</span><span><span>(</span><span>vUv</span><span>,</span><span> PI </span><span>*</span><span> </span></span><span>0.25</span><span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div><div><div><div>2</div></div><div><span>vec2</span><span><span> lightUvX </span><span>=</span><span> </span></span><span>vec2</span><span><span>(rotatedUv.x </span><span>*</span><span> </span></span><span>0.1</span><span><span> </span><span>+</span><span> </span></span><span>0.45</span><span><span>, rotatedUv.y </span><span>*</span><span> </span></span><span>0.5</span><span><span> </span><span>+</span><span> </span></span><span>0.25</span><span>);</span></div></div><div><div><div>3</div></div><div><span>float</span><span><span> lightX </span><span>=</span><span> </span></span><span>0.015</span><span><span> </span><span>/</span><span> </span></span><span>distance</span><span>(lightUvX, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div><div><div><div>4</div></div><div><span>vec2</span><span><span> lightUvY </span><span>=</span><span> </span></span><span>vec2</span><span><span>(rotatedUv.y </span><span>*</span><span> </span></span><span>0.1</span><span><span> </span><span>+</span><span> </span></span><span>0.45</span><span><span>, rotatedUv.x </span><span>*</span><span> </span></span><span>0.5</span><span><span> </span><span>+</span><span> </span></span><span>0.25</span><span>);</span></div></div><div><div><div>5</div></div><div><span>float</span><span><span> lightY </span><span>=</span><span> </span></span><span>0.015</span><span><span> </span><span>/</span><span> </span></span><span>distance</span><span>(lightUvY, </span><span>vec2</span><span>(</span><span>0.5</span><span>));</span></div></div><div><div><div>6</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> lightX </span><span>*</span><span> lightY;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Pattern 33<a href="#pattern-33"><span>#</span></a></h2><p>基于 pattern 27，取阈值，可以得到圆形</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.25</span><span>, </span><span>distance</span><span><span>(</span><span>vUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span>)));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423161640441_1745396201.png" alt="image-20250423161640441" /><figcaption>image-20250423161640441</figcaption></figure><p></p></section>
<section><h2>pattern 34<a href="#pattern-34"><span>#</span></a></h2><p>基于 pattern 27 取绝对值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>vUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>)) </span><span>-</span><span> </span></span><span>0.25</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423161819875_1745396300.png" alt="image-20250423161819875" /><figcaption>image-20250423161819875</figcaption></figure><p></p></section>
<section><h2>Pattern 35<a href="#pattern-35"><span>#</span></a></h2><p>pattern 33 与 pattern 34 结合，得到圆环</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.01</span><span>, </span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>vUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>))</span><span> </span><span>-</span><span> </span></span><span>0.25</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423161946930_1745396388.png" alt="image-20250423161946930" /><figcaption>image-20250423161946930</figcaption></figure><p></p></section>
<section><h2>Pattern 36<a href="#pattern-36"><span>#</span></a></h2><p>pattern 35 取反</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>step</span><span>(</span><span>0.01</span><span>, </span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>vUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>))</span><span> </span><span>-</span><span> </span></span><span>0.25</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423162051274_1745396452.png" alt="image-20250423162051274" /><figcaption>image-20250423162051274</figcaption></figure><p></p></section>
<section><h2>Pattern 37<a href="#pattern-37"><span>#</span></a></h2><p>将 pattern 36 的圆环，设置竖直方向的变化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> wavedUv </span><span>=</span><span> </span></span><span>vec2</span><span><span>(vUv.x, vUv.y </span><span>+</span><span> </span></span><span>sin</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>30.0</span><span><span>) </span><span>*</span><span> </span></span><span>0.1</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>step</span><span>(</span><span>0.01</span><span>, </span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>wavedUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>))</span><span> </span><span>-</span><span> </span></span><span>0.25</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423165630788_1745398591.png" alt="image-20250423165630788" /><figcaption>image-20250423165630788</figcaption></figure><p></p></section>
<section><h2>Pattern 38<a href="#pattern-38"><span>#</span></a></h2><p>将 pattern 37 同样设置水平和竖直的变化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> wavedUv </span><span>=</span><span> </span></span><span>vec2</span><span><span>(vUv.x </span><span>+</span><span> </span></span><span>sin</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>30.0</span><span><span>) </span><span>*</span><span> </span></span><span>0.1</span><span><span>, vUv.y </span><span>+</span><span> </span></span><span>sin</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>30.0</span><span><span>) </span><span>*</span><span> </span></span><span>0.1</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>step</span><span>(</span><span>0.01</span><span>, </span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>wavedUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>))</span><span> </span><span>-</span><span> </span></span><span>0.25</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423165801595_1745398682.png" alt="image-20250423165801595" /><figcaption>image-20250423165801595</figcaption></figure><p></p></section>
<section><h2>Pattern 40<a href="#pattern-40"><span>#</span></a></h2><p>将 pattern 38 的变化周期增大。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec2</span><span><span> wavedUv </span><span>=</span><span> </span></span><span>vec2</span><span><span>(vUv.x </span><span>+</span><span> </span></span><span>sin</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>100.0</span><span><span>) </span><span>*</span><span> </span></span><span>0.1</span><span><span>, vUv.y </span><span>+</span><span> </span></span><span>sin</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>100.0</span><span><span>) </span><span>*</span><span> </span></span><span>0.1</span><span>);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>step</span><span>(</span><span>0.01</span><span>, </span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>wavedUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>))</span><span> </span><span>-</span><span> </span></span><span>0.25</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423165911909_1745398753.png" alt="image-20250423165911909" /><figcaption>image-20250423165911909</figcaption></figure><p></p></section>
<section><h2>Pattern 41<a href="#pattern-41"><span>#</span></a></h2><p>uv坐标取反正切<code>atan()</code>,f(x)=tan^(-1)(x)的取值范围是[-π, π]，uv坐标没有负值取值为[0, π]</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> angle </span><span>=</span><span> </span></span><span>atan</span><span>(vUv.x, vUv.y);</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> angle;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423171135603_1745399496.png" alt="image-20250423171135603" /><figcaption>image-20250423171135603</figcaption></figure><p></p></section>
<section><h2>Pattern 42<a href="#pattern-42"><span>#</span></a></h2><p>pattern 41 的uv坐标置于中心 -0.5</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> angle </span><span>=</span><span> </span></span><span>atan</span><span><span>(vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span><span>, vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span> );</span></div></div><div><div><div>2</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> angle;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423171322719_1745399603.png" alt="image-20250423171322719" /><figcaption>image-20250423171322719</figcaption></figure><p></p></section>
<section><h2>Pattern 43<a href="#pattern-43"><span>#</span></a></h2><p>由(0.5, 0.5)开始的角度渐变纹理</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> angle </span><span>=</span><span> </span></span><span>atan</span><span><span>(vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span><span>, vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>);</span></div></div><div><div><div>2</div></div><div><span><span>angle </span><span>/=</span><span> PI </span><span>*</span><span> </span></span><span>2.0</span><span>;</span><span> // 映射到 [0, 1]</span></div></div><div><div><div>3</div></div><div><span><span>angle </span><span>+=</span><span> </span></span><span>0.5</span><span>;</span></div></div><div><div><div>4</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> angle;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423172014681_1745400015.png" alt="image-20250423172014681" /><figcaption>image-20250423172014681</figcaption></figure><p></p></section>
<section><h2>Pattern 44<a href="#pattern-44"><span>#</span></a></h2><p>pattern 43 取模分段</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> angle </span><span>=</span><span> </span></span><span>atan</span><span><span>(vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span><span>, vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>);</span></div></div><div><div><div>2</div></div><div><span><span>angle </span><span>/=</span><span> PI </span><span>*</span><span> </span></span><span>2.0</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>angle </span><span>+=</span><span> </span></span><span>0.5</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>angle </span><span>*=</span><span> </span></span><span>20.0</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>angle </span><span>=</span><span> </span></span><span>mod</span><span>(angle, </span><span>1.0</span><span>);</span></div></div><div><div><div>6</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> angle;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423172130152_1745400091.png" alt="image-20250423172130152" /><figcaption>image-20250423172130152</figcaption></figure><p></p></section>
<section><h2>Pattern 45<a href="#pattern-45"><span>#</span></a></h2><p>pattern 40 和 Pattern 43 组合</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> angle </span><span>=</span><span> </span></span><span>atan</span><span><span>(vUv.x </span><span>-</span><span> </span></span><span>0.5</span><span><span>, vUv.y </span><span>-</span><span> </span></span><span>0.5</span><span>);</span></div></div><div><div><div>2</div></div><div><span><span>angle </span><span>/=</span><span> PI </span><span>*</span><span> </span></span><span>2.0</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>angle </span><span>+=</span><span> </span></span><span>0.5</span><span>;</span></div></div><div><div><div>4</div></div><div><span>float</span><span><span> sinusoid </span><span>=</span><span> </span></span><span>sin</span><span><span>(angle </span><span>*</span><span> </span></span><span>100.0</span><span>);</span></div></div><div><div><div>5</div></div><div><span>float</span><span><span> radius </span><span>=</span><span> </span></span><span>0.25</span><span><span> </span><span>+</span><span> sinusoid </span><span>*</span><span> </span></span><span>0.01</span><span>;</span></div></div><div><div><div>6</div></div><div><span>// 计算到中心点(0.5, 0.5)的距离，sqrt((x-0.5)^2 + (y-0.5)^2)</span></div></div><div><div><div>7</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>step</span><span>(</span><span>0.01</span><span>, </span><span>abs</span><span>(</span><span>distance</span><span><span>(</span><span>vUv</span><span>,</span><span> </span></span><span>vec2</span><span>(</span><span>0.5</span><span><span>))</span><span> </span><span>-</span><span> radius</span><span>));</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423172902748_1745400543.png" alt="image-20250423172902748" /><figcaption>image-20250423172902748</figcaption></figure><p></p></section>
<section><h2>Pattern 46 perlin noise<a href="#pattern-46-perlin-noise"><span>#</span></a></h2><p>perlin noise是自然噪声生成算法，具有在函数上的连续性，并可在多次调用时给出一致的数值。用来生成噪声图样，生成随机性、云朵，水面的倒影等等。<a href="https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83" target="_blank">perlin noise</a></p><p>下面是其中一种算法， Classic Perlin 2D Noise</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//  Classic Perlin 2D Noise</span></div></div><div><div><div>2</div></div><div><span>//  by Stefan Gustavson (https://github.com/stegu/webgl-noise)</span></div></div><div><div><div>3</div></div><div><span>//</span></div></div><div><div><div>4</div></div><div><span>vec2</span><span> </span><span>fade</span><span><span>(</span><span>vec2 </span><span>t</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span><span> t </span><span>*</span><span> t </span><span>*</span><span> t </span><span>*</span><span> (t </span><span>*</span><span> (t </span><span>*</span><span> </span></span><span>6.0</span><span><span> </span><span>-</span><span> </span></span><span>15.0</span><span><span>) </span><span>+</span><span> </span></span><span>10.0</span><span>);</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>vec4</span><span> </span><span>permute</span><span><span>(</span><span>vec4 </span><span>x</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>return</span><span> </span><span>mod</span><span><span>(((x </span><span>*</span><span> </span></span><span>34.0</span><span><span>) </span><span>+</span><span> </span></span><span>1.0</span><span><span>) </span><span>*</span><span> x, </span></span><span>289.0</span><span>);</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div><span>float</span><span> </span><span>cnoise</span><span><span>(</span><span>vec2 </span><span>P</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>vec4 Pi </span><span>=</span><span> </span><span>floor</span><span>(</span><span>P</span><span>.</span><span>xyxy</span><span><span>) </span><span>+</span><span> </span></span><span>vec4</span><span>(</span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>1.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>vec4 Pf </span><span>=</span><span> </span><span>fract</span><span>(</span><span>P</span><span>.</span><span>xyxy</span><span><span>) </span><span>-</span><span> </span></span><span>vec4</span><span>(</span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>1.0</span><span>, </span><span>1.0</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>Pi </span><span>=</span><span> </span><span>mod</span><span>(Pi, </span><span>289.0</span><span>);</span><span> // To avoid truncation effects in permutation</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>vec4 ix </span><span>=</span><span> </span><span>Pi</span><span>.</span><span>xzxz</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>vec4 iy </span><span>=</span><span> </span><span>Pi</span><span>.</span><span>yyww</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>vec4 fx </span><span>=</span><span> </span><span>Pf</span><span>.</span><span>xzxz</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>vec4 fy </span><span>=</span><span> </span><span>Pf</span><span>.</span><span>yyww</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>vec4 i </span><span>=</span><span> </span><span>permute</span><span>(</span><span>permute</span><span><span>(ix) </span><span>+</span><span> iy);</span></span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>vec4 gx </span><span>=</span><span> </span><span>2.0</span><span><span> </span><span>*</span><span> </span></span><span>fract</span><span><span>(i </span><span>*</span><span> </span></span><span>0.0243902439</span><span><span>) </span><span>-</span><span> </span></span><span>1.0</span><span>;</span><span> // 1/41 = 0.024...</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>vec4 gy </span><span>=</span><span> </span><span>abs</span><span><span>(gx) </span><span>-</span><span> </span></span><span>0.5</span><span>;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>vec4 tx </span><span>=</span><span> </span><span>floor</span><span><span>(gx </span><span>+</span><span> </span></span><span>0.5</span><span>);</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>gx </span><span>=</span><span> gx </span><span>-</span><span> tx;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>vec2 g00 </span><span>=</span><span> </span><span>vec2</span><span>(</span><span>gx</span><span>.</span><span>x</span><span>, </span><span>gy</span><span>.</span><span>x</span><span>);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>vec2 g10 </span><span>=</span><span> </span><span>vec2</span><span>(</span><span>gx</span><span>.</span><span>y</span><span>, </span><span>gy</span><span>.</span><span>y</span><span>);</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>vec2 g01 </span><span>=</span><span> </span><span>vec2</span><span>(</span><span>gx</span><span>.</span><span>z</span><span>, </span><span>gy</span><span>.</span><span>z</span><span>);</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>vec2 g11 </span><span>=</span><span> </span><span>vec2</span><span>(</span><span>gx</span><span>.</span><span>w</span><span>, </span><span>gy</span><span>.</span><span>w</span><span>);</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>vec4 norm </span><span>=</span><span> </span><span>1.79284291400159</span><span><span> </span><span>-</span><span> </span></span><span>0.85373472095314</span><span><span> </span><span>*</span></span></div></div><div><div><div>28</div></div><div><span>        </span><span>vec4</span><span>(</span><span>dot</span><span>(g00, g00), </span><span>dot</span><span>(g01, g01), </span><span>dot</span><span>(g10, g10), </span><span>dot</span><span>(g11, g11));</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>g00 </span><span>*=</span><span> </span><span>norm</span><span>.</span><span>x</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>g01 </span><span>*=</span><span> </span><span>norm</span><span>.</span><span>y</span><span>;</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>g10 </span><span>*=</span><span> </span><span>norm</span><span>.</span><span>z</span><span>;</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>g11 </span><span>*=</span><span> </span><span>norm</span><span>.</span><span>w</span><span>;</span></div></div><div><div><div>33</div></div><div><span>    </span><span>float</span><span><span> n00 </span><span>=</span><span> </span></span><span>dot</span><span>(g00, </span><span>vec2</span><span>(</span><span>fx</span><span>.</span><span>x</span><span>, </span><span>fy</span><span>.</span><span>x</span><span>));</span></div></div><div><div><div>34</div></div><div><span>    </span><span>float</span><span><span> n10 </span><span>=</span><span> </span></span><span>dot</span><span>(g10, </span><span>vec2</span><span>(</span><span>fx</span><span>.</span><span>y</span><span>, </span><span>fy</span><span>.</span><span>y</span><span>));</span></div></div><div><div><div>35</div></div><div><span>    </span><span>float</span><span><span> n01 </span><span>=</span><span> </span></span><span>dot</span><span>(g01, </span><span>vec2</span><span>(</span><span>fx</span><span>.</span><span>z</span><span>, </span><span>fy</span><span>.</span><span>z</span><span>));</span></div></div><div><div><div>36</div></div><div><span>    </span><span>float</span><span><span> n11 </span><span>=</span><span> </span></span><span>dot</span><span>(g11, </span><span>vec2</span><span>(</span><span>fx</span><span>.</span><span>w</span><span>, </span><span>fy</span><span>.</span><span>w</span><span>));</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>vec2 fade_xy </span><span>=</span><span> </span><span>fade</span><span>(</span><span>Pf</span><span>.</span><span>xy</span><span>);</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>vec2 n_x </span><span>=</span><span> </span><span>mix</span><span>(</span><span>vec2</span><span>(n00, n01), </span><span>vec2</span><span>(n10, n11), </span><span>fade_xy</span><span>.</span><span>x</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>float</span><span><span> n_xy </span><span>=</span><span> </span></span><span>mix</span><span>(</span><span>n_x</span><span>.</span><span>x</span><span>, </span><span>n_x</span><span>.</span><span>y</span><span>, </span><span>fade_xy</span><span>.</span><span>y</span><span>);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>return</span><span> </span><span>2.3</span><span><span> </span><span>*</span><span> n_xy;</span></span></div></div><div><div><div>41</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>main()</code>使用</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>cnoise</span><span><span>(</span><span>vUv </span><span>*</span><span> </span></span><span>10.0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423174617343_1745401578.png" alt="image-20250423174617343" /><figcaption>image-20250423174617343</figcaption></figure><p></p></section>
<section><h2>Pattern 47<a href="#pattern-47"><span>#</span></a></h2><p><code>cnoise()</code>返回的是正数和负数,<code>step()</code>取值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.0</span><span>, </span><span>cnoise</span><span><span>(</span><span>vUv </span><span>*</span><span> </span></span><span>10.0</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423174831480_1745401713.png" alt="image-20250423174831480" /><figcaption>image-20250423174831480</figcaption></figure><p></p></section>
<section><h2>Pattern 48<a href="#pattern-48"><span>#</span></a></h2><p><code>ads()</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>1.0</span><span><span> </span><span>-</span><span> </span></span><span>abs</span><span>(</span><span>cnoise</span><span><span>(</span><span>vUv </span><span>*</span><span> </span></span><span>10.0</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423174923302_1745401764.png" alt="image-20250423174923302" /><figcaption>image-20250423174923302</figcaption></figure><p></p></section>
<section><h2>pattern 49<a href="#pattern-49"><span>#</span></a></h2><p>线条变化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>sin</span><span>(</span><span>cnoise</span><span><span>(</span><span>vUv </span><span>*</span><span> </span></span><span>10.0</span><span><span>) </span><span>*</span><span> </span></span><span>20.0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423174959444_1745401800.png" alt="image-20250423174959444" /><figcaption>image-20250423174959444</figcaption></figure><p></p></section>
<section><h2>pattern 50<a href="#pattern-50"><span>#</span></a></h2><p>pattern 49 取 <code>step()</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.9</span><span>, </span><span>sin</span><span>(</span><span>cnoise</span><span><span>(</span><span>vUv </span><span>*</span><span> </span></span><span>10.0</span><span><span>)</span><span> </span><span>*</span><span> </span></span><span>20.0</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423175040761_1745401841.png" alt="image-20250423175040761" /><figcaption>image-20250423175040761</figcaption></figure><p></p></section>
<section><h2>Mix Color<a href="#mix-color"><span>#</span></a></h2><p>混合颜色，通过<code>mix()</code>混合颜色</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.9</span><span>, </span><span>sin</span><span>(</span><span>cnoise</span><span><span>(</span><span>vUv </span><span>*</span><span> </span></span><span>10.0</span><span><span>)</span><span> </span><span>*</span><span> </span></span><span>20.0</span><span>));</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>vec3</span><span><span> blackColor </span><span>=</span><span> </span></span><span>vec3</span><span>(</span><span>0.0</span><span>);</span><span> // black</span></div></div><div><div><div>4</div></div><div><span>vec3</span><span><span> uvColor </span><span>=</span><span> </span></span><span>vec3</span><span>(vUv, </span><span>1.0</span><span>);</span></div></div><div><div><div>5</div></div><div><span>vec3</span><span><span> mixedColor </span><span>=</span><span> </span></span><span>mix</span><span>(blackColor, uvColor, strength);</span><span> // 混合颜色</span></div></div><div><div><div>6</div></div><div><span><span>gl_FragColor </span><span>=</span><span> </span></span><span>vec4</span><span>(mixedColor, </span><span>1.0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423175503213_1745402104.png" alt="image-20250423175503213" /><figcaption>image-20250423175503213</figcaption></figure><p></p><p>但是在 pattern 11 中，图案叠加时<code>strength</code>超过了1，混合是第三个值会超过1，颜色的值被计算到更大的范围，所以需要限制界限</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>float</span><span><span> strength </span><span>=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.x </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>2</div></div><div><span><span>strength </span><span>+=</span><span> </span></span><span>step</span><span>(</span><span>0.8</span><span>, </span><span>mod</span><span><span>(</span><span>vUv.y </span><span>*</span><span> </span></span><span>10.0</span><span><span>,</span><span> </span></span><span>1.0</span><span>));</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>vec3</span><span><span> blackColor </span><span>=</span><span> </span></span><span>vec3</span><span>(</span><span>0.0</span><span>);</span><span> // black</span></div></div><div><div><div>5</div></div><div><span>vec3</span><span><span> uvColor </span><span>=</span><span> </span></span><span>vec3</span><span>(vUv, </span><span>1.0</span><span>);</span></div></div><div><div><div>6</div></div><div><span>vec3</span><span><span> mixedColor </span><span>=</span><span> </span></span><span>mix</span><span>(blackColor, uvColor, strength);</span><span> // 混合颜色</span></div></div><div><div><div>7</div></div><div><span><span>gl_FragColor </span><span>=</span><span> </span></span><span>vec4</span><span>(mixedColor, </span><span>1.0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>交点处颜色明显更亮，因为 <code>strength &gt; 1.0</code></p><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423175559671_1745402160.png" alt="image-20250423175559671" /><figcaption>image-20250423175559671</figcaption></figure><p></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>strength </span><span>=</span><span> </span></span><span>clamp</span><span>(strength, </span><span>0.0</span><span>, </span><span>1.0</span><span>);</span><span> // 限制范围在0-1之间</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://raw.githubusercontent.com/sukksa/upgit-images/main/2025/04/image-20250423175613542_1745402174.png" alt="image-20250423175613542" /><figcaption>image-20250423175613542</figcaption></figure><p></p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/di-yi-ge-shader-cheng-xu/</id>
      <title type="text">第一个 shader 程序</title>
      <published>2025-05-07T01:10:38.000Z</published>
      <updated>2025-05-07T01:10:38.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/di-yi-ge-shader-cheng-xu/"/>
      <summary type="text">第一个 shader程序，glsl 的使用</summary>
      <content type="html"><![CDATA[<p>shader 是用 GLSL(OpenGL Shading Language) 编写的程序，他会被发送到GPU运行。原生 WebGL 的一部分，如果不依赖任何库使用 WebGl 的话，需要自己动手创建 Shader。shader 会为几何体的每个顶点(vertex)定位，以及为几何体的所有可见片段（fragment）着色。</p>
<p>我们会为shader发送大量的数据，比如顶点坐标、网格变换信息、摄像机的信息以及几何体的顶点颜色、纹理等等。然后交给CPU，利用GLSL编写的着色器程序处理这些数据，最终在屏幕上定位顶点，并为每个可见片段着色</p>
<p>着色器有两种类型</p>
<ul>
<li>vertex shader</li>
<li>fragment shader</li>
</ul>
<p>Vertex Shader</p>
<p>顶点着色器会为每个几何体的顶点定位，当顶点着色器为顶点定位完之后，GPU 便知道哪些几何体的像素是可见的，然后进入片段着色器(fragment shader)</p>
<p>fragment shader</p>
<p>片段着色器会为顶点着色，包括颜色，纹理，透明度等等。</p>
<p><a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">https://learnopengl.com/Getting-started/Coordinate-Systems</a></p>
<img src="https://learnopengl.com/img/getting-started/coordinate_systems.png" alt="img" />
<p>为什么要自己编写着色器？为什么我们不能直接使用Threejs的内置材质？</p>
<ul>
<li>材质不够灵活，一些形状通过内置的material无法实现</li>
<li>着色器高效，比Threejs需要性能开销更少</li>
<li>可以做后期处理</li>
</ul>
<p>Threejs有两种着色器<code>ShaderMaterial</code> <code>RawShaderMaterial</code>。<code>ShaderMaterial</code>包含了必要的<code>uniforms</code>和<code>attributes</code>的声明，除此之外并无差别。<code>RawShaderMaterial</code>就是原始的，必须编写所有代码。</p>
<section><h2>RawShaderMaterial<a href="#rawshadermaterial"><span>#</span></a></h2><p><code>THREE.RawShaderMaterial</code>创建原始着色器,<code>RawShaderMaterial</code> 不会自动注入 Three.js 的内置着色器代码（如光照、矩阵变换等），开发者需要手动管理所有 Uniforms 和 Attributes。</p><p>分别创建<code>vertex.glsl</code>和<code>fragment.glsl</code>文件, 使用 <code>vite-plugin-glsl</code>或者<code>vite-plugin-glslify</code>插件，可以将大的着色器分割为小的模块，可以重用着色器块，以及可以使用其他开发者编写的着色器块.</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span> </span><span>npm</span><span> </span><span>install</span><span> </span><span>vite-plugin-glsl</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在<code>vite.config.js</code>中导入glsl</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>glsl</span><span> </span><span>from</span><span> </span><span>'vite-plugin-glsl'</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>4</div></div><div><span>  </span><span>plugins</span><span>:</span><span> [</span></div></div><div><div><div>5</div></div><div><span>    </span><span>glsl</span><span>(),</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用</p><div><div><div><figure><figcaption><span>script.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>// Geometry</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>geometry</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>PlaneGeometry</span><span>(</span><span>1</span><span>, </span><span>1</span><span>, </span><span>32</span><span>, </span><span>32</span><span>)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>count</span><span> </span><span>=</span><span><span> </span><span>geometry</span><span>.</span></span><span>attributes</span><span>.</span><span>position</span><span>.</span><span>count</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>randoms</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Float32Array</span><span><span>(</span><span>count</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>count</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>randoms</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>random</span><span>()</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div><span><span>geometry</span><span>.</span></span><span>setAttribute</span><span>(</span><span>'aRandom'</span><span>, </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>BufferAttribute</span><span><span>(</span><span>randoms</span><span>, </span></span><span>1</span><span>))</span></div></div><div><div><div>10</div></div><div><span>// Material</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>material</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>RawShaderMaterial</span><span>({</span></div></div><div><div><div>12</div></div><div><span>  </span><span>vertexShader</span><span>:</span><span><span> </span><span>testVertexShader</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>  </span><span>fragmentShader</span><span>:</span><span><span> </span><span>testFragmentShader</span><span>,</span></span></div></div><div><div><div>14</div></div><div><span>  </span><span>transparent</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>15</div></div><div><span>  </span><span>side</span><span>:</span><span> </span><span>THREE</span><span>.</span><span>DoubleSide</span><span>,</span></div></div><div><div><div>16</div></div><div><span>  </span><span>// wireframe: true,</span></div></div><div><div><div>17</div></div><div><span>  </span><span>uniforms</span><span>:</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>uFrequency</span><span>:</span><span> { </span><span>value</span><span>:</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>Vector2</span><span>(</span><span>10</span><span>, </span><span>5</span><span>) },</span></div></div><div><div><div>19</div></div><div><span>    </span><span>uTime</span><span>:</span><span> { </span><span>value</span><span>:</span><span> </span><span>0</span><span> },</span></div></div><div><div><div>20</div></div><div><span>    </span><span>uColor</span><span>:</span><span> { </span><span>value</span><span>:</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>Color</span><span>(</span><span>0x7799cc</span><span>) },</span></div></div><div><div><div>21</div></div><div><span>    </span><span>uTexture</span><span>:</span><span> { </span><span>value</span><span>:</span><span><span> </span><span>flagTexture</span><span> },</span></span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>23</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在<code>THREE.RawShaderMaterial</code>中，THREEjs中的material同样可以使用<code>wireframe</code>, <code>side</code>, <code>transparent</code>, <code>flatShading</code>这些属性。但是像<code>map</code>，<code>alphaMap</code>,<code>opacity</code>等等都没法使用，因为这些事在顶点内部处理的，只有在<code>fragmentShader</code>中才能更改</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>count</span><span> </span><span>=</span><span><span> </span><span>geometry</span><span>.</span></span><span>attributes</span><span>.</span><span>position</span><span>.</span><span>count</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>randoms</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Float32Array</span><span><span>(</span><span>count</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>count</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>randoms</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>random</span><span>()</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span><span>geometry</span><span>.</span></span><span>setAttribute</span><span>(</span><span>'aRandom'</span><span>, </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>BufferAttribute</span><span><span>(</span><span>randoms</span><span>, </span></span><span>1</span><span>))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面创建了一个随机生成的名为<code>aRandom</code>的<code>attribute</code>，添加在<code>geometry</code>的<code>attribute</code>上，在<code>vertex.glsl</code>中可以通过<code>attribute float aRandom;</code>接收。</p><p>因为<code>new THREE.BufferAttribute(randoms, 1)</code>为 1，表明一个为一组。所以是<code>float</code>类型</p><p>通过<code>gui</code>直接操作shader中数据，可以通过更改<code>uniforms</code>中的值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>gui</span><span>.</span></span><span>add</span><span><span>(</span><span>material</span><span>.</span></span><span>uniforms</span><span>.</span><span>uFrequency</span><span>.</span><span>value</span><span>, </span><span>'x'</span><span>).</span><span>min</span><span>(</span><span>0</span><span>).</span><span>max</span><span>(</span><span>20</span><span>).</span><span>step</span><span>(</span><span>0.01</span><span>).</span><span>name</span><span>(</span><span>'uFrequency.x'</span><span>)</span></div></div><div><div><div>2</div></div><div><span><span>gui</span><span>.</span></span><span>add</span><span><span>(</span><span>material</span><span>.</span></span><span>uniforms</span><span>.</span><span>uFrequency</span><span>.</span><span>value</span><span>, </span><span>'y'</span><span>).</span><span>min</span><span>(</span><span>0</span><span>).</span><span>max</span><span>(</span><span>20</span><span>).</span><span>step</span><span>(</span><span>0.01</span><span>).</span><span>name</span><span>(</span><span>'uFrequency.y'</span><span>)</span></div></div><div><div><div>3</div></div><div><span><span>gui</span><span>.</span></span><span>addColor</span><span><span>(</span><span>material</span><span>.</span></span><span>uniforms</span><span>.</span><span>uColor</span><span>, </span><span>'value'</span><span>).</span><span>name</span><span>(</span><span>'uColor'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>vertex.glsl<a href="#vertexglsl"><span>#</span></a></h2><p>他会对每个顶点执行这段代码</p><div><div><div><figure><figcaption><span>vertex.glsl</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> projectionMatrix;</span></div></div><div><div><div>2</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> viewMatrix;</span></div></div><div><div><div>3</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> modelMatrix;</span></div></div><div><div><div>4</div></div><div><span>uniform</span><span> </span><span>vec2</span><span> uFrequency;</span></div></div><div><div><div>5</div></div><div><span>uniform</span><span> </span><span>float</span><span> uTime;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>attribute</span><span> </span><span>vec3</span><span> position;</span></div></div><div><div><div>8</div></div><div><span>attribute</span><span> </span><span>vec2</span><span> uv;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>11</div></div><div><span>varying</span><span> </span><span>float</span><span> vElevation;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>void</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>vec4 modelPosition </span><span>=</span><span> modelMatrix </span><span>*</span><span> </span><span>vec4</span><span>(position, </span><span>1.0</span><span>);</span></div></div><div><div><div>15</div></div><div><span>  </span><span>float</span><span><span> elevation </span><span>=</span><span> </span></span><span>sin</span><span>(</span><span>modelPosition</span><span>.</span><span>x</span><span><span> </span><span>*</span><span> </span></span><span>uFrequency</span><span>.</span><span>x</span><span><span> </span><span>-</span><span> uTime) </span><span>*</span><span> </span></span><span>0.1</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>elevation </span><span>+=</span><span> </span><span>sin</span><span>(</span><span>modelPosition</span><span>.</span><span>y</span><span><span> </span><span>*</span><span> </span></span><span>uFrequency</span><span>.</span><span>y</span><span><span> </span><span>-</span><span> uTime) </span><span>*</span><span> </span></span><span>0.1</span><span>;</span></div></div><div><div><div>17</div></div><div><span>  </span><span>modelPosition</span><span>.</span><span>z</span><span><span> </span><span>+=</span><span> elevation;</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>vec4 viewPosition </span><span>=</span><span> viewMatrix </span><span>*</span><span> modelPosition;</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>vec4 projectionPosition </span><span>=</span><span> projectionMatrix </span><span>*</span><span> viewPosition;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>gl_Position </span><span>=</span><span> projectionPosition;</span></div></div><div><div><div>23</div></div><div><span>  </span><span>// gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>vUv </span><span>=</span><span> uv;</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>vElevation </span><span>=</span><span> elevation;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>gl_Position</code> 为什么是一个<code>vec4</code>?</p><p>这是因为我们的坐标实际上是在裁剪空间里，裁剪空间不是一个二维空间，是一个四维的投影变换，将3D场景转换为2D屏幕坐标。当我们从camera观察其中一个物体时，最终渲染是在2d的canvas上的，除了知道他的顶点坐标(x,y,z)，还需要知道他的深度，帮助计算机判断物体哪个顶点坐标在前，形成透视效果。在正交投影中，<code>w</code> 分量通常为 <code>1</code>，因此不影响结果。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> projectionMatrix;</span></div></div><div><div><div>2</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> viewMatrix;</span></div></div><div><div><div>3</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> modelMatrix;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>uniform 变量是全局常量，在整个渲染过程中保持不变。它的作用是从 CPU 侧的应用程序向 GPU 中的着色器程序传递数据，例如变换矩阵、光照参数、时间等。uniform 变量可以同时在顶点着色器和片段着色器中使用，并且一旦设置，在一次渲染调用中，所有顶点和片段的 uniform 变量值都是相同的，且在着色器内部不能被修改。</p><p>上面三个矩阵都是 uniform 的，不会改变，对几何体的每个顶点都采用相同的矩阵运算</p><ul>
<li>
<p><code>uniform mat4 modelMatrix;</code></p>
<p>模型矩阵，他将对每个顶点应用模型自身的变换（平移、旋转、缩放），将顶点从模型本地坐标系转换到世界坐标系。</p>
<p><code>mesh.position.x = 1</code>就会变换这个矩阵，只需要提供(position, rotation, scale)</p>
</li>
<li>
<p><code>uniform mat4 viewMatrix;</code></p>
<p>相机矩阵，他定义相机的位姿（位置和朝向），将顶点从世界坐标系转换到相机坐标系。</p>
<p>同理，改变camera的 position, rotation, near, far都会变换这个矩阵</p>
</li>
<li>
<p><code>uniform mat4 projectionMatrix;</code></p>
<p>投影矩阵，它负责将3D场景从相机视角（观察空间）投影到2D裁剪空间。</p>
</li>
</ul><p>也可以传递自定义的属性。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>uniform</span><span> </span><span>vec2</span><span> uFrequency;</span></div></div><div><div><div>2</div></div><div><span>uniform</span><span> </span><span>float</span><span> uTime;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在<code>RawShaderMaterial</code>中通过<code>uniforms</code>可以传递<code>uniform</code>类型的数据</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>material</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>RawShaderMaterial</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>3</div></div><div><span>  </span><span>uniforms</span><span>:</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>uFrequency</span><span>:</span><span> { </span><span>value</span><span>:</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>Vector2</span><span>(</span><span>10</span><span>, </span><span>5</span><span>) },</span></div></div><div><div><div>5</div></div><div><span>    </span><span>uTime</span><span>:</span><span> { </span><span>value</span><span>:</span><span> </span><span>0</span><span> },</span></div></div><div><div><div>6</div></div><div><span>    </span><span>uColor</span><span>:</span><span> { </span><span>value</span><span>:</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>Color</span><span>(</span><span>0x7799cc</span><span>) },</span></div></div><div><div><div>7</div></div><div><span>    </span><span>uTexture</span><span>:</span><span> { </span><span>value</span><span>:</span><span><span> </span><span>flagTexture</span><span> },</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面就像顶点着色器和片段着色器传递了四个<code>uniform</code>类型的数据</p><p>attribute (属性)是顶点间变化的数据。主要功能是从顶点缓冲区读取每个顶点的数据。这些数据通常包含顶点的位置、法线、纹理坐标等信息。它是专门为顶点着色器设计的，因为它存储的是每个顶点特有的数据。在顶点着色器执行过程中，会逐个处理每个顶点的 attribute 数据。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>attribute</span><span> </span><span>vec3</span><span> position;</span></div></div><div><div><div>2</div></div><div><span>attribute</span><span> </span><span>vec2</span><span> uv;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这个值从实例化的<code>THREE.xxxGeometry</code>中 <code>geometry.attributes.position</code>中的每个坐标点取得</p><p><code>attribute vec2 uv;</code>同样的接收一个uv坐标，也可以通过<code>geometry.setAttribute('random', new THREE.BufferAttribute(randoms, 1))</code>添加一组名为 <code>random</code>的<code>attribute</code>,同样通过<code>attribute float random;</code>获取</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div><span>varying</span><span> </span><span>float</span><span> vElevation;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>varying 变量充当了顶点着色器和片段着色器之间的数据桥梁。顶点着色器会为每个顶点计算 varying 变量的值，在光栅化阶段，这些值会在三角形的各个片段之间进行插值。片段着色器会接收这些插值后的值，并以此进行后续的计算。简而言之，varying 变量将顶点级别的数据传递到了片段着色器。</p><p>上面代码就向片段着色器传递了 <code>vec2 vUv</code>和 <code>float vElevation</code>的值，在<code>fragment.glsl</code>中用相同的方式接收。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>vec4</span><span> </span><span>modelPosition</span><span> </span><span>=</span><span><span> </span><span>modelMatrix</span><span> </span></span><span>*</span><span> </span><span>vec4</span><span><span>(</span><span>position</span><span>, </span></span><span>1.0</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>float</span><span> </span><span>elevation</span><span> </span><span>=</span><span> </span><span>sin</span><span><span>(</span><span>modelPosition</span><span>.</span></span><span>x</span><span> </span><span>*</span><span><span> </span><span>uFrequency</span><span>.</span></span><span>x</span><span> </span><span>-</span><span><span> </span><span>uTime</span><span>) </span></span><span>*</span><span> </span><span>0.1</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>elevation</span><span> </span><span>+=</span><span> </span><span>sin</span><span><span>(</span><span>modelPosition</span><span>.</span></span><span>y</span><span> </span><span>*</span><span><span> </span><span>uFrequency</span><span>.</span></span><span>y</span><span> </span><span>-</span><span><span> </span><span>uTime</span><span>) </span></span><span>*</span><span> </span><span>0.1</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>modelPosition</span><span>.</span><span>z</span><span> </span><span>+=</span><span><span> </span><span>elevation</span><span>;</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>vec4</span><span> </span><span>viewPosition</span><span> </span><span>=</span><span><span> </span><span>viewMatrix</span><span> </span></span><span>*</span><span><span> </span><span>modelPosition</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>vec4</span><span> </span><span>projectionPosition</span><span> </span><span>=</span><span><span> </span><span>projectionMatrix</span><span> </span></span><span>*</span><span><span> </span><span>viewPosition</span><span>;</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>gl_Position</span><span> </span><span>=</span><span><span> </span><span>projectionPosition</span><span>;</span></span></div></div><div><div><div>11</div></div><div><span>  </span><span>// gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>vUv</span><span> </span><span>=</span><span><span> </span><span>uv</span><span>;</span></span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>vElevation</span><span> </span><span>=</span><span><span> </span><span>elevation</span><span>;</span></span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>main()</code>函数是整个程序的入口，对每个顶点都会执行这段代码。</p><p><code>gl_Position</code>是顶点着色器的内置输出变量，用于存储顶点的最终裁剪空间坐标。这个坐标会被后续的图形管线用于裁剪、透视除法和视口变换到屏幕空间。</p><p>每个矩阵都会对 <code>gl_Position</code>进行变换，最终时将顶点定位在裁剪空间内。所以在渲染时我们会应用不同的矩阵，直到获得裁剪空间的顶点坐标。</p><blockquote><p>在每次顶点着色器运行结束时，OpenGL 期望坐标在特定范围内，并且任何超出此范围的坐标都将被剪裁。被剪切的坐标将被丢弃，因此剩余的坐标最终将在屏幕上显示为片段。这也是 clip space (裁剪空间)名称的由来。</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>gl_Position </span><span>=</span><span> projectionMatrix </span><span>*</span><span> viewMatrix </span><span>*</span><span> modelMatrix </span><span>*</span><span> </span></span><span>vec4</span><span>(position, </span><span>1.0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因为矩阵乘法是右乘计算的，<strong>实际计算顺序</strong>：<code>modelMatrix</code> → <code>viewMatrix</code> → <code>projectionMatrix</code>。组合顺序不能变，等价于下面的代码</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec4</span><span><span> modelPosition </span><span>=</span><span> modelMatrix </span><span>*</span><span> </span></span><span>vec4</span><span>(position, </span><span>1.0</span><span>);</span></div></div><div><div><div>2</div></div><div><span>vec4</span><span><span> viewPosition </span><span>=</span><span> viewMatrix </span><span>*</span><span> modelPosition;</span></span></div></div><div><div><div>3</div></div><div><span>vec4</span><span><span> projectionPosition </span><span>=</span><span> projectionMatrix </span><span>*</span><span> viewPosition;</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>gl_Position </span><span>=</span><span> projectionPosition;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>fragment.glsl<a href="#fragmentglsl"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>precision mediump </span><span>float</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>uniform</span><span> </span><span>vec3</span><span> uColor;</span></div></div><div><div><div>4</div></div><div><span>// 声明一个 uniform sampler2D，用于表示可以在片段着色器中采样的纹理</span></div></div><div><div><div>5</div></div><div><span>uniform</span><span> </span><span>sampler2D</span><span> uTexture;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>8</div></div><div><span>varying</span><span> </span><span>float</span><span> vElevation;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>void</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// 第一个参数是纹理，第二个参数是在纹理的哪个位置着色也就是uv坐标</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>vec4 textrueColor </span><span>=</span><span> </span><span>texture2D</span><span>(uTexture, vUv);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>textrueColor</span><span>.</span><span>rgb</span><span><span> </span><span>*=</span><span> vElevation </span><span>*</span><span> </span></span><span>2.0</span><span><span> </span><span>+</span><span> </span></span><span>0.5</span><span>;</span><span> // [0.3, 0.7]</span></div></div><div><div><div>14</div></div><div><span>    </span><span>// gl_FragColor = vec4(uColor, 1.0);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>gl_FragColor </span><span>=</span><span> </span><span>vec4</span><span>(textrueColor);</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>precision mediump </span><span>float</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>定义浮点数的精度：<code>highp</code>，<code>mediump</code>，<code>lowp</code></p><p>为什么需要？当你在处理非常大、非常小的数值时，或者需要大幅缩小场景，然后放大到场景中的一个很小的物体上，可能会影响渲染效果。</p><p><code>highp</code>会影响性能，而且低端设备上可能无法支持；<code>lowp</code>可能因为缺乏精度引发错误。所以通常使用 <code>mediump</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>uniform</span><span> </span><span>sampler2D</span><span> uTexture;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>申明一个 uniform的 sampler2D(2D textrue)类型，存储了纹理数据</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>varying</span><span> </span><span>vec2</span><span> vUv;</span></div></div><div><div><div>2</div></div><div><span>varying</span><span> </span><span>float</span><span> vElevation;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>从 vertex.glsl 中接收插值<code>vec2 vUv</code>和<code>float vElevation</code></p><p>fragment shader 不能直接获取<code>attribute</code>，如果要获取uv坐标，需要通过 vertex shader，发送给 fragment shader 接收，并且要取别名不能重复声明变量。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vec4</span><span><span> textrueColor </span><span>=</span><span> </span></span><span>texture2D</span><span>(uTexture, vUv);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>texture2D(sampler2D sampler, vec2 uv)</code> 是一个用于从2D纹理中采样颜色值的内置函数，根据给定的纹理坐标从绑定的纹理中获取对应的颜色数据。为3D模型的每个顶点指定UV坐标，告诉渲染器：“将纹理图像中<code>(u, v)</code>位置的颜色，应用到该顶点对应的模型表面区域”。</p><p>输入两个参数，第一个参数是纹理，第二个参数是在纹理的哪个位置着色也就是uv坐标；输出一个 <code>vec4</code> 类型的颜色值（RGBA格式）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>gl_FragColor </span><span>=</span><span> </span></span><span>vec4</span><span>(uColor, </span><span>1.0</span><span>);</span><span> // 应用 material.uniforms.uColor 的颜色</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>gl_FragColor </span><span>=</span><span> </span></span><span>vec4</span><span>(textrueColor);</span><span> // 应用采样后的纹理颜色</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>gl_FragColor = vec4(r, g, b, a)</code>给每个可见片段上色，直接决定了渲染到屏幕（或帧缓冲）的像素颜色。若要更改透明度(a)时，需要给<code>material</code>设置 <code>transparent: true</code></p><p>如果想查看某些值，我们可以发送给<code>gl_FragColor</code>显示，例如查看uv <code>gl_FragColor = vec4(vUv, 1.0, 1.0);</code></p></section>
<section><h2>ShaderMaterial<a href="#shadermaterial"><span>#</span></a></h2><p><code>ShaderMaterial</code>和<code>RawShaderMaterial</code>基本相同，只不过<code>ShaderMaterial</code>提前加入了一些声明：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> projectionMatrix;</span></div></div><div><div><div>2</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> viewMatrix;</span></div></div><div><div><div>3</div></div><div><span>uniform</span><span> </span><span>mat4</span><span> modelMatrix;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>attribute</span><span> </span><span>vec3</span><span> position;</span></div></div><div><div><div>6</div></div><div><span>attribute</span><span> </span><span>vec2</span><span> uv;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>precision mediump </span><span>float</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>RawShaderMaterial</code>中所有<code>uniforms</code>和<code>attributes</code>需用户手动声明和赋值。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/threemodules/</id>
      <title type="text">THREE Modules</title>
      <published>2025-04-10T08:34:08.000Z</published>
      <updated>2025-04-10T08:34:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/threemodules/"/>
      <summary type="text">将THREE项目通过Class划分为不同的模块</summary>
      <content type="html"><![CDATA[<p>export</p>
<div><figure><figcaption><span>test.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>somethingToExport</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>name</span><span>:</span><span> </span><span>'test'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>export</span><span> { </span><span>somethingToExport</span><span> }</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// script.js</span></div></div><div><div><div>7</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>test</span><span> </span><span>from</span><span> </span><span>'./test'</span></div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>test</span><span>.</span></span><span>somethingToExport</span><span>)</span></div></div><div><div><div>9</div></div><div><span>// or</span></div></div><div><div><div>10</div></div><div><span>import</span><span> { </span><span>somethingToExport</span><span> } </span><span>from</span><span> </span><span>'./test'</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>somethingToExport</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<div><figure><figcaption><span>test.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>somethingToExport</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>name</span><span>:</span><span> </span><span>'test'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>somethingToExport</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// script.js</span></div></div><div><div><div>7</div></div><div><span>import</span><span> </span><span>somethingToExport</span><span> </span><span>from</span><span> </span><span>'./test.js'</span></div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>somethingToExport</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<hr />
<section><h1>Experience Class<a href="#experience-class"><span>#</span></a></h1><p>首先创建一个<code>Experience</code>类，将所有与 WebGL 相关的内容全部放在里面。大的类都在这儿实例化，并且设置为单例类。只有在第一次实例化时，会创建一个实例，若已经创建过实例对象，则会返回那个实例对象。类似于<code>vuex</code>一个全局变量</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Sizes</span><span> </span><span>from</span><span> </span><span>'./Utils/Sizes.js'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>Time</span><span> </span><span>from</span><span> </span><span>'./Utils/Time.js'</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>Debug</span><span> </span><span>from</span><span> </span><span>'./Utils/Debug.js'</span></div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>Camera</span><span> </span><span>from</span><span> </span><span>'./Camera.js'</span></div></div><div><div><div>6</div></div><div><span>import</span><span> </span><span>Renderer</span><span> </span><span>from</span><span> </span><span>'./Renderer.js'</span></div></div><div><div><div>7</div></div><div><span>import</span><span> </span><span>World</span><span> </span><span>from</span><span> </span><span>'./World/World.js'</span></div></div><div><div><div>8</div></div><div><span>import</span><span> </span><span>Resources</span><span> </span><span>from</span><span> </span><span>'./Utils/Resources.js'</span></div></div><div><div><div>9</div></div><div><span>import</span><span> </span><span>sources</span><span> </span><span>from</span><span> </span><span>'./sources.js'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>let</span><span><span> </span><span>instance</span><span> </span></span><span>=</span><span> </span><span>null</span></div></div><div><div><div>12</div></div><div><span>// 单例模式，确保只有一个Experience实例存在</span></div></div><div><div><div>13</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Experience</span><span> {</span></div></div><div><div><div>14</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>canvas</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span> (</span><span>instance</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>      </span><span>return</span><span> </span><span>instance</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>    </span><span>instance</span><span> </span><span>=</span><span> </span><span>this</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 添加为全局变量，方便控制台查看，可加可不加</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>this</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>// Options</span></div></div><div><div><div>24</div></div><div><span>    </span><span>this</span><span>.</span><span>canvas</span><span> </span><span>=</span><span> </span><span>canvas</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// setup</span></div></div><div><div><div>27</div></div><div><span>    </span><span>this</span><span>.</span><span>debug</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Debug</span><span>()</span></div></div><div><div><div>28</div></div><div><span>    </span><span>this</span><span>.</span><span>sizes</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Sizes</span><span>()</span></div></div><div><div><div>29</div></div><div><span>    </span><span>this</span><span>.</span><span>time</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Time</span><span>()</span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>Scene</span><span>()</span></div></div><div><div><div>31</div></div><div><span>    </span><span>this</span><span>.</span><span>resources</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Resources</span><span>(</span><span>sources</span><span>)</span></div></div><div><div><div>32</div></div><div><span>    </span><span>this</span><span>.</span><span>camera</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Camera</span><span>()</span></div></div><div><div><div>33</div></div><div><span>    </span><span>this</span><span>.</span><span>renderer</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Renderer</span><span>()</span></div></div><div><div><div>34</div></div><div><span>    </span><span>this</span><span>.</span><span>world</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>World</span><span>()</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>// EventEmitter的方法</span></div></div><div><div><div>37</div></div><div><span>    </span><span>// 监听窗口大小变化事件，调用resize方法</span></div></div><div><div><div>38</div></div><div><span>    </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>on</span><span>(</span><span>'resize'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>39</div></div><div><span>      </span><span>this</span><span>.</span><span>resize</span><span>()</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>41</div></div><div><span>    </span><span>// 监听时间变化事件，调用update方法</span></div></div><div><div><div>42</div></div><div><span>    </span><span>this</span><span>.</span><span>time</span><span>.</span><span>on</span><span>(</span><span>'tick'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>43</div></div><div><span>      </span><span>this</span><span>.</span><span>update</span><span>()</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>  </span><span>resize</span><span>() {</span></div></div><div><div><div>48</div></div><div><span>    </span><span>this</span><span>.</span><span>camera</span><span>.</span><span>resize</span><span>()</span></div></div><div><div><div>49</div></div><div><span>    </span><span>this</span><span>.</span><span>renderer</span><span>.</span><span>resize</span><span>()</span></div></div><div><div><div>50</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>51</div></div><div><span>  </span><span>update</span><span>() {</span></div></div><div><div><div>52</div></div><div><span>    </span><span>this</span><span>.</span><span>camera</span><span>.</span><span>update</span><span>()</span></div></div><div><div><div>53</div></div><div><span>    </span><span>this</span><span>.</span><span>world</span><span>.</span><span>update</span><span>()</span></div></div><div><div><div>54</div></div><div><span>    </span><span>// render 放在最后，确保所有物体都渲染完成</span></div></div><div><div><div>55</div></div><div><span>    </span><span>this</span><span>.</span><span>renderer</span><span>.</span><span>update</span><span>()</span></div></div><div><div><div>56</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>  </span><span>destroy</span><span>() {</span></div></div><div><div><div>59</div></div><div><span>    </span><span>// EventEmitter的方法，移除事件监听</span></div></div><div><div><div>60</div></div><div><span>    </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>off</span><span>(</span><span>'resize'</span><span>)</span></div></div><div><div><div>61</div></div><div><span>    </span><span>this</span><span>.</span><span>time</span><span>.</span><span>off</span><span>(</span><span>'tick'</span><span>)</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>// 遍历场景中的所有物体</span></div></div><div><div><div>64</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>traverse</span><span><span>(</span><span>child</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>65</div></div><div><span>      </span><span>// 检查物体是否是Mesh</span></div></div><div><div><div>66</div></div><div><span>      </span><span>if</span><span> (</span><span>child</span><span> </span><span>instanceof</span><span> </span><span>THREE</span><span>.</span><span>Mesh</span><span>) {</span></div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>child</span><span>.</span><span>geometry</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>        </span><span>// 遍历网格模型的材质，调用dispose方法</span></div></div><div><div><div>70</div></div><div><span>        </span><span>for</span><span> (</span><span>const</span><span> </span><span>key</span><span> </span><span>in</span><span><span> </span><span>child</span><span>.</span></span><span>material</span><span>) {</span></div></div><div><div><div>71</div></div><div><span>          </span><span>const</span><span> </span><span>value</span><span> </span><span>=</span><span><span> </span><span>child</span><span>.</span></span><span>material</span><span>[</span><span>key</span><span>]</span></div></div><div><div><div>72</div></div><div><span>          </span><span>if</span><span> (</span><span>value</span><span> </span><span>&amp;&amp;</span><span> </span><span>typeof</span><span><span> </span><span>value</span><span>.</span></span><span>dispose</span><span> </span><span>===</span><span> </span><span>'function'</span><span>) {</span></div></div><div><div><div>73</div></div><div><span><span>            </span></span><span>value</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>74</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>75</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>76</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>77</div></div><div>
</div></div><div><div><div>78</div></div><div><span>      </span><span>this</span><span>.</span><span>camera</span><span>.</span><span>controls</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>79</div></div><div><span>      </span><span>this</span><span>.</span><span>renderer</span><span>.</span><span>instance</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>80</div></div><div><span>      </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) </span><span>this</span><span>.</span><span>debug</span><span>.</span><span>ui</span><span>.</span><span>destroy</span><span>()</span></div></div><div><div><div>81</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>82</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>83</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h1>Utils<a href="#utils"><span>#</span></a></h1><p>将 THREEjs 项目通过 class 和 modules 分割为许多小模块方便维护。</p><section><h2>Size Class<a href="#size-class"><span>#</span></a></h2><p>size 类，记录窗口大小并且监听<code>resize</code>事件。</p><p>通知其他类 size 发生了变化<a href="https://gist.github.com/brunosimon/120acda915e6629e3a4d497935b16bdf" target="_blank">EventEmitter</a>，首先继承<code>class Sizes extends EventEmitter</code>，事件触发时，调用<code>this.trigger('event')</code>，然后再捕获变化 <code>this.sizes.on(()=&gt;{...})</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>EventEmitter</span><span> </span><span>from</span><span> </span><span>'./EventEmitter.js'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Sizes</span><span> </span><span>extends</span><span> </span><span>EventEmitter</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>super</span><span>()</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>width</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>innerWidth</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>height</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>innerHeight</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>pixelRatio</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>window</span><span>.</span></span><span>devicePixelRatio</span><span>, </span><span>2</span><span>)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>addEventListener</span><span>(</span><span>'resize'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>      </span><span>this</span><span>.</span><span>width</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>innerWidth</span></div></div><div><div><div>11</div></div><div><span>      </span><span>this</span><span>.</span><span>height</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>innerHeight</span></div></div><div><div><div>12</div></div><div><span>      </span><span>this</span><span>.</span><span>pixelRatio</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>window</span><span>.</span></span><span>devicePixelRatio</span><span>, </span><span>2</span><span>)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>      </span><span>// 使用事件触发器来通知其他模块尺寸发生了变化</span></div></div><div><div><div>15</div></div><div><span>      </span><span>this</span><span>.</span><span>trigger</span><span>(</span><span>'resize'</span><span>)</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Time Class<a href="#time-class"><span>#</span></a></h2><p>time 类，记录时间相关的参数，并触发<code>window.requestAnimationFrame</code>渲染页面</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>EventEmitter</span><span> </span><span>from</span><span> </span><span>'./EventEmitter'</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Time</span><span> </span><span>extends</span><span> </span><span>EventEmitter</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>super</span><span>()</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>start</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>() </span><span>// 记录开始时间</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>current</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>start</span><span> </span><span>// 记录当前时间</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>elapsed</span><span> </span><span>=</span><span> </span><span>0</span><span> </span><span>// 记录从开始到现在的时间</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>delta</span><span> </span><span>=</span><span> </span><span>16</span><span> </span><span>// 每帧大约时间间隔，默认值为16ms（60fps）</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>requestAnimationFrame</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>      </span><span>this</span><span>.</span><span>tick</span><span>()</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>tick</span><span>() {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>const</span><span> </span><span>currentTime</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>()</span></div></div><div><div><div>18</div></div><div><span>    </span><span>this</span><span>.</span><span>delta</span><span> </span><span>=</span><span> </span><span>currentTime</span><span> </span><span>-</span><span> </span><span>this</span><span>.</span><span>current</span></div></div><div><div><div>19</div></div><div><span>    </span><span>this</span><span>.</span><span>current</span><span> </span><span>=</span><span> </span><span>currentTime</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>elapsed</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>current</span><span> </span><span>-</span><span> </span><span>this</span><span>.</span><span>start</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// 触发事件，通知其他模块时间发生了变化</span></div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>trigger</span><span>(</span><span>'tick'</span><span>)</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>requestAnimationFrame</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>26</div></div><div><span>      </span><span>this</span><span>.</span><span>tick</span><span>()</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Resources Class<a href="#resources-class"><span>#</span></a></h2><p>统一加载资源，方便管理。在这个类中实例化所有需要的<code>loader</code>，以及加载<code>asset</code>。</p><p>在所有的资源都加载完成之后，告诉其他类资源已经加载完毕</p><p>首先创建一个<code>sources.js</code>文件，存放资源的路径</p><div><figure><figcaption><span>sources.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> [</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>3</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'environmentMapTexture'</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>type</span><span>:</span><span> </span><span>'cubeTexture'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>path</span><span>:</span><span> [</span></div></div><div><div><div>6</div></div><div><span>      </span><span>'textures/environmentMap/px.jpg'</span><span>,</span></div></div><div><div><div>7</div></div><div><span>      </span><span>'textures/environmentMap/nx.jpg'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>      </span><span>'textures/environmentMap/py.jpg'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>'textures/environmentMap/ny.jpg'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>      </span><span>'textures/environmentMap/pz.jpg'</span><span>,</span></div></div><div><div><div>11</div></div><div><span>      </span><span>'textures/environmentMap/nz.jpg'</span><span>,</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>],</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>14</div></div><div><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 Resources Class 中，有三个属性</p><ul>
<li><code>items</code> 已加载的资源 <code>{}</code></li>
<li><code>toLoad</code> 需要加载的资源数量 <code>this.sources.length</code></li>
<li><code>loaded</code> 已加载的资源数量 <code>0</code></li>
</ul><p>如果<code>toLoad == loaded</code>表明所有资源都加载完毕了</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>GLTFLoader</span><span> } </span><span>from</span><span> </span><span>'three/addons/loaders/GLTFLoader.js'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>EventEmitter</span><span> </span><span>from</span><span> </span><span>'./EventEmitter'</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Resources</span><span> </span><span>extends</span><span> </span><span>EventEmitter</span><span> {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>sources</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>super</span><span>()</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>sources</span><span> </span><span>=</span><span> </span><span>sources</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>items</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>this</span><span>.</span><span>toLoad</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>sources</span><span>.</span><span>length</span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>loaded</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>this</span><span>.</span><span>setLoaders</span><span>()</span></div></div><div><div><div>15</div></div><div><span>    </span><span>this</span><span>.</span><span>startLoading</span><span>()</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>  </span><span>// 加载需要的loader</span></div></div><div><div><div>18</div></div><div><span>  </span><span>setLoaders</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>    </span><span>this</span><span>.</span><span>loaders</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>loaders</span><span>.</span><span>gltfLoader</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>GLTFLoader</span><span>()</span></div></div><div><div><div>21</div></div><div><span>    </span><span>this</span><span>.</span><span>loaders</span><span>.</span><span>textureLoader</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>TextureLoader</span><span>()</span></div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>loaders</span><span>.</span><span>cubeTextureLoader</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>CubeTextureLoader</span><span>()</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>  </span><span>// 通过loader加载资源</span></div></div><div><div><div>25</div></div><div><span>  </span><span>startLoading</span><span>() {</span></div></div><div><div><div>26</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> </span><span>source</span><span> </span><span>of</span><span> </span><span>this</span><span>.</span><span>sources</span><span>) {</span></div></div><div><div><div>27</div></div><div><span>      </span><span>switch</span><span><span> (</span><span>source</span><span>.</span></span><span>type</span><span>) {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>case</span><span> </span><span>'gltfModel'</span><span>:</span></div></div><div><div><div>29</div></div><div><span>          </span><span>this</span><span>.</span><span>loaders</span><span>.</span><span>gltfLoader</span><span>.</span><span>load</span><span><span>(</span><span>source</span><span>.</span></span><span>path</span><span><span>, </span><span>file</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>30</div></div><div><span>            </span><span>this</span><span>.</span><span>sourceLoaded</span><span>(</span><span>source</span><span>, </span><span>file</span><span>)</span></div></div><div><div><div>31</div></div><div><span><span>          </span></span><span>})</span></div></div><div><div><div>32</div></div><div><span>          </span><span>break</span></div></div><div><div><div>33</div></div><div><span>        </span><span>case</span><span> </span><span>'texture'</span><span>:</span></div></div><div><div><div>34</div></div><div><span>          </span><span>this</span><span>.</span><span>loaders</span><span>.</span><span>textureLoader</span><span>.</span><span>load</span><span><span>(</span><span>source</span><span>.</span></span><span>path</span><span><span>, </span><span>file</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>35</div></div><div><span>            </span><span>this</span><span>.</span><span>sourceLoaded</span><span>(</span><span>source</span><span>, </span><span>file</span><span>)</span></div></div><div><div><div>36</div></div><div><span><span>          </span></span><span>})</span></div></div><div><div><div>37</div></div><div><span>          </span><span>break</span></div></div><div><div><div>38</div></div><div><span>        </span><span>case</span><span> </span><span>'cubeTexture'</span><span>:</span></div></div><div><div><div>39</div></div><div><span>          </span><span>this</span><span>.</span><span>loaders</span><span>.</span><span>cubeTextureLoader</span><span>.</span><span>load</span><span><span>(</span><span>source</span><span>.</span></span><span>path</span><span><span>, </span><span>file</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>40</div></div><div><span>            </span><span>this</span><span>.</span><span>sourceLoaded</span><span>(</span><span>source</span><span>, </span><span>file</span><span>)</span></div></div><div><div><div>41</div></div><div><span><span>          </span></span><span>})</span></div></div><div><div><div>42</div></div><div><span>          </span><span>break</span></div></div><div><div><div>43</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>46</div></div><div><span>  </span><span>// 资源加载后，存储到items中，并判断是否全部加载完成</span></div></div><div><div><div>47</div></div><div><span>  </span><span>sourceLoaded</span><span><span>(</span><span>source</span><span>, </span><span>file</span><span>) {</span></span></div></div><div><div><div>48</div></div><div><span>    </span><span>this</span><span>.</span><span>items</span><span><span>[</span><span>source</span><span>.</span></span><span>name</span><span>] </span><span>=</span><span> </span><span>file</span></div></div><div><div><div>49</div></div><div><span>    </span><span>this</span><span>.</span><span>loaded</span><span>++</span></div></div><div><div><div>50</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>loaded</span><span> </span><span>===</span><span> </span><span>this</span><span>.</span><span>toLoad</span><span>) {</span></div></div><div><div><div>51</div></div><div><span>      </span><span>this</span><span>.</span><span>trigger</span><span>(</span><span>'ready'</span><span>)</span></div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>53</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Debug Class<a href="#debug-class"><span>#</span></a></h2><p>lil-gui设置debug。</p><p>在url后面加上<code>#debug</code>会打开调试窗口，通过判断 <code>window.location.hash === '#debug'</code>，如果是才创建<code>G】gui</code>的实例</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>GUI</span><span> </span><span>from</span><span> </span><span>'lil-gui'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Debug</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 如果包含debug，则创建一个GUI实例</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>active</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>location</span><span>.</span><span>hash</span><span> </span><span>===</span><span> </span><span>'#debug'</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>      </span><span>this</span><span>.</span><span>ui</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>GUI</span><span>()</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>并在 <code>Experience Class</code>中实例化 <code>this.debug = new Debug()</code></p><p>在实例化的类中添加debug object，以 <code>Environment Class</code>为例。在<code>constructor()</code>中创建environment分类，<code>setEnvironmentMap()</code>方法中添加debug选项。在Environment Class可看</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Environment</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>debug</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>debug</span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>debug</span><span>.</span><span>ui</span><span>.</span><span>addFolder</span><span>(</span><span>'environment'</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>10</div></div><div><span>  </span><span>setEnvironmentMap</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>updateMaterials</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> { </span><span>...</span><span> }</span></div></div><div><div><div>13</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>updateMaterials</span><span>()</span></div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>environmentMap</span><span>, </span><span>'intensity'</span><span>)</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>.</span><span>min</span><span>(</span><span>0</span><span>)</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>.</span><span>max</span><span>(</span><span>4</span><span>)</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>.</span><span>step</span><span>(</span><span>0.001</span><span>)</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>.</span><span>onChange</span><span>(</span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>updateMaterials</span><span>)</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>EventEmitter Class<a href="#eventemitter-class"><span>#</span></a></h2><p>事件监听类，在需要监听的class直接继承</p><ul>
<li><code>trigger()</code>告诉其他模块触发事件</li>
<li><code>on()</code> 接收事件触发，执行操作</li>
<li><code>off()</code> 移除事件监听</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Sizes</span><span> </span><span>extends</span><span> </span><span>EventEmitter</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 使用事件触发器来通知其他模块尺寸发生了变化</span></div></div><div><div><div>3</div></div><div><span>  </span><span>this</span><span>.</span><span>trigger</span><span>(</span><span>'resize'</span><span>)</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 监听窗口大小变化事件，调用resize方法</span></div></div><div><div><div>7</div></div><div><span>this</span><span>.</span><span>sizes</span><span>.</span><span>on</span><span>(</span><span>'resize'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>this</span><span>.</span><span>resize</span><span>()</span></div></div><div><div><div>9</div></div><div><span>})</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 移除事件监听</span></div></div><div><div><div>12</div></div><div><span>this</span><span>.</span><span>sizes</span><span>.</span><span>off</span><span>(</span><span>'resize'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><a href="https://gist.github.com/brunosimon/120acda915e6629e3a4d497935b16bdf" target="_blank">EventEmitter</a></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>EventEmitter</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>callbacks</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>callbacks</span><span>.</span><span>base</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>on</span><span><span>(</span><span>_names</span><span>, </span><span>callback</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>// Errors</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span> </span><span>_names</span><span> </span><span>===</span><span> </span><span>'undefined'</span><span> </span><span>||</span><span> </span><span>_names</span><span> </span><span>===</span><span> </span><span>''</span><span>) {</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>'wrong names'</span><span>)</span></div></div><div><div><div>11</div></div><div><span>      </span><span>return</span><span> </span><span>false</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span> </span><span>callback</span><span> </span><span>===</span><span> </span><span>'undefined'</span><span>) {</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>'wrong callback'</span><span>)</span></div></div><div><div><div>16</div></div><div><span>      </span><span>return</span><span> </span><span>false</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// Resolve names</span></div></div><div><div><div>20</div></div><div><span>    </span><span>const</span><span> </span><span>names</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resolveNames</span><span>(</span><span>_names</span><span>)</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// Each name</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>names</span><span>.</span><span>forEach</span><span><span>(</span><span>_name</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>24</div></div><div><span>      </span><span>// Resolve name</span></div></div><div><div><div>25</div></div><div><span>      </span><span>const</span><span> </span><span>name</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resolveName</span><span>(</span><span>_name</span><span>)</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>      </span><span>// Create namespace if not exist</span></div></div><div><div><div>28</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span>(</span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>] </span><span>instanceof</span><span> </span><span>Object</span><span>))</span></div></div><div><div><div>29</div></div><div><span>        </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>] </span><span>=</span><span> {}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>      </span><span>// Create callback if not exist</span></div></div><div><div><div>32</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span>(</span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>] </span><span>instanceof</span><span> </span><span>Array</span><span>))</span></div></div><div><div><div>33</div></div><div><span>        </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>] </span><span>=</span><span> []</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>      </span><span>// Add callback</span></div></div><div><div><div>36</div></div><div><span>      </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>].</span><span>push</span><span>(</span><span>callback</span><span>)</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>return</span><span> </span><span>this</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>  </span><span>off</span><span><span>(</span><span>_names</span><span>) {</span></span></div></div><div><div><div>43</div></div><div><span>    </span><span>// Errors</span></div></div><div><div><div>44</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span> </span><span>_names</span><span> </span><span>===</span><span> </span><span>'undefined'</span><span> </span><span>||</span><span> </span><span>_names</span><span> </span><span>===</span><span> </span><span>''</span><span>) {</span></div></div><div><div><div>45</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>'wrong name'</span><span>)</span></div></div><div><div><div>46</div></div><div><span>      </span><span>return</span><span> </span><span>false</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>// Resolve names</span></div></div><div><div><div>50</div></div><div><span>    </span><span>const</span><span> </span><span>names</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resolveNames</span><span>(</span><span>_names</span><span>)</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>// Each name</span></div></div><div><div><div>53</div></div><div><span><span>    </span></span><span>names</span><span>.</span><span>forEach</span><span><span>(</span><span>_name</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>54</div></div><div><span>      </span><span>// Resolve name</span></div></div><div><div><div>55</div></div><div><span>      </span><span>const</span><span> </span><span>name</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resolveName</span><span>(</span><span>_name</span><span>)</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>      </span><span>// Remove namespace</span></div></div><div><div><div>58</div></div><div><span>      </span><span>if</span><span><span> (</span><span>name</span><span>.</span></span><span>namespace</span><span> </span><span>!==</span><span> </span><span>'base'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>name</span><span>.</span></span><span>value</span><span> </span><span>===</span><span> </span><span>''</span><span>) {</span></div></div><div><div><div>59</div></div><div><span>        </span><span>delete</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>]</span></div></div><div><div><div>60</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>      </span><span>// Remove specific callback in namespace</span></div></div><div><div><div>63</div></div><div><span>      </span><span>else</span><span> {</span></div></div><div><div><div>64</div></div><div><span>        </span><span>// Default</span></div></div><div><div><div>65</div></div><div><span>        </span><span>if</span><span><span> (</span><span>name</span><span>.</span></span><span>namespace</span><span> </span><span>===</span><span> </span><span>'base'</span><span>) {</span></div></div><div><div><div>66</div></div><div><span>          </span><span>// Try to remove from each namespace</span></div></div><div><div><div>67</div></div><div><span>          </span><span>for</span><span> (</span><span>const</span><span> </span><span>namespace</span><span> </span><span>in</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span>) {</span></div></div><div><div><div>68</div></div><div><span>            </span><span>if</span><span> (</span></div></div><div><div><div>69</div></div><div><span>              </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span>] </span><span>instanceof</span><span> </span><span>Object</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>70</div></div><div><span>              </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>] </span><span>instanceof</span><span> </span><span>Array</span></div></div><div><div><div>71</div></div><div><span><span>            </span></span><span>) {</span></div></div><div><div><div>72</div></div><div><span>              </span><span>delete</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>]</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>              </span><span>// Remove namespace if empty</span></div></div><div><div><div>75</div></div><div><span>              </span><span>if</span><span><span> (</span><span>Object</span><span>.</span></span><span>keys</span><span>(</span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span>]).</span><span>length</span><span> </span><span>===</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>76</div></div><div><span>                </span><span>delete</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span>]</span></div></div><div><div><div>77</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>78</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>79</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>        </span><span>// Specified namespace</span></div></div><div><div><div>82</div></div><div><span>        </span><span>else</span><span> </span><span>if</span><span> (</span></div></div><div><div><div>83</div></div><div><span>          </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>] </span><span>instanceof</span><span> </span><span>Object</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>84</div></div><div><span>          </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>] </span><span>instanceof</span><span> </span><span>Array</span></div></div><div><div><div>85</div></div><div><span><span>        </span></span><span>) {</span></div></div><div><div><div>86</div></div><div><span>          </span><span>delete</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>]</span></div></div><div><div><div>87</div></div><div>
</div></div><div><div><div>88</div></div><div><span>          </span><span>// Remove namespace if empty</span></div></div><div><div><div>89</div></div><div><span>          </span><span>if</span><span><span> (</span><span>Object</span><span>.</span></span><span>keys</span><span>(</span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>]).</span><span>length</span><span> </span><span>===</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>90</div></div><div><span>            </span><span>delete</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>]</span></div></div><div><div><div>91</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>92</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>93</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>94</div></div><div>
</div></div><div><div><div>95</div></div><div><span>    </span><span>return</span><span> </span><span>this</span></div></div><div><div><div>96</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>  </span><span>trigger</span><span><span>(</span><span>_name</span><span>, </span><span>_args</span><span>) {</span></span></div></div><div><div><div>99</div></div><div><span>    </span><span>// Errors</span></div></div><div><div><div>100</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span> </span><span>_name</span><span> </span><span>===</span><span> </span><span>'undefined'</span><span> </span><span>||</span><span> </span><span>_name</span><span> </span><span>===</span><span> </span><span>''</span><span>) {</span></div></div><div><div><div>101</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>'wrong name'</span><span>)</span></div></div><div><div><div>102</div></div><div><span>      </span><span>return</span><span> </span><span>false</span></div></div><div><div><div>103</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>104</div></div><div>
</div></div><div><div><div>105</div></div><div><span>    </span><span>let</span><span> </span><span>finalResult</span><span> </span><span>=</span><span> </span><span>null</span></div></div><div><div><div>106</div></div><div><span>    </span><span>let</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>null</span></div></div><div><div><div>107</div></div><div>
</div></div><div><div><div>108</div></div><div><span>    </span><span>// Default args</span></div></div><div><div><div>109</div></div><div><span>    </span><span>const</span><span> </span><span>args</span><span> </span><span>=</span><span> </span><span>!</span><span>(</span><span>_args</span><span> </span><span>instanceof</span><span> </span><span>Array</span><span>) </span><span>?</span><span> [] </span><span>:</span><span> </span><span>_args</span></div></div><div><div><div>110</div></div><div>
</div></div><div><div><div>111</div></div><div><span>    </span><span>// Resolve names (should on have one event)</span></div></div><div><div><div>112</div></div><div><span>    </span><span>let</span><span> </span><span>name</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resolveNames</span><span>(</span><span>_name</span><span>)</span></div></div><div><div><div>113</div></div><div>
</div></div><div><div><div>114</div></div><div><span>    </span><span>// Resolve name</span></div></div><div><div><div>115</div></div><div><span>    </span><span>name</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resolveName</span><span>(</span><span>name</span><span>[</span><span>0</span><span>])</span></div></div><div><div><div>116</div></div><div>
</div></div><div><div><div>117</div></div><div><span>    </span><span>// Default namespace</span></div></div><div><div><div>118</div></div><div><span>    </span><span>if</span><span><span> (</span><span>name</span><span>.</span></span><span>namespace</span><span> </span><span>===</span><span> </span><span>'base'</span><span>) {</span></div></div><div><div><div>119</div></div><div><span>      </span><span>// Try to find callback in each namespace</span></div></div><div><div><div>120</div></div><div><span>      </span><span>for</span><span> (</span><span>const</span><span> </span><span>namespace</span><span> </span><span>in</span><span> </span><span>this</span><span>.</span><span>callbacks</span><span>) {</span></div></div><div><div><div>121</div></div><div><span>        </span><span>if</span><span> (</span></div></div><div><div><div>122</div></div><div><span>          </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span>] </span><span>instanceof</span><span> </span><span>Object</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>123</div></div><div><span>          </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>] </span><span>instanceof</span><span> </span><span>Array</span></div></div><div><div><div>124</div></div><div><span><span>        </span></span><span>) {</span></div></div><div><div><div>125</div></div><div><span>          </span><span>this</span><span>.</span><span>callbacks</span><span>[</span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>].</span><span>forEach</span><span>(</span><span>function</span><span><span> (</span><span>callback</span><span>) {</span></span></div></div><div><div><div>126</div></div><div><span>            </span><span>result</span><span> </span><span>=</span><span><span> </span><span>callback</span><span>.</span></span><span>apply</span><span>(</span><span>this</span><span>, </span><span>args</span><span>)</span></div></div><div><div><div>127</div></div><div>
</div></div><div><div><div>128</div></div><div><span>            </span><span>if</span><span> (</span><span>typeof</span><span> </span><span>finalResult</span><span> </span><span>===</span><span> </span><span>'undefined'</span><span>) {</span></div></div><div><div><div>129</div></div><div><span>              </span><span>finalResult</span><span> </span><span>=</span><span> </span><span>result</span></div></div><div><div><div>130</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>131</div></div><div><span><span>          </span></span><span>})</span></div></div><div><div><div>132</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>133</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>134</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>135</div></div><div>
</div></div><div><div><div>136</div></div><div><span>    </span><span>// Specified namespace</span></div></div><div><div><div>137</div></div><div><span>    </span><span>else</span><span> </span><span>if</span><span> (</span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span>] </span><span>instanceof</span><span> </span><span>Object</span><span>) {</span></div></div><div><div><div>138</div></div><div><span>      </span><span>if</span><span><span> (</span><span>name</span><span>.</span></span><span>value</span><span> </span><span>===</span><span> </span><span>''</span><span>) {</span></div></div><div><div><div>139</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>'wrong name'</span><span>)</span></div></div><div><div><div>140</div></div><div><span>        </span><span>return</span><span> </span><span>this</span></div></div><div><div><div>141</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>142</div></div><div>
</div></div><div><div><div>143</div></div><div><span>      </span><span>this</span><span>.</span><span>callbacks</span><span><span>[</span><span>name</span><span>.</span></span><span>namespace</span><span><span>][</span><span>name</span><span>.</span></span><span>value</span><span>].</span><span>forEach</span><span>(</span><span>function</span><span><span> (</span><span>callback</span><span>) {</span></span></div></div><div><div><div>144</div></div><div><span>        </span><span>result</span><span> </span><span>=</span><span><span> </span><span>callback</span><span>.</span></span><span>apply</span><span>(</span><span>this</span><span>, </span><span>args</span><span>)</span></div></div><div><div><div>145</div></div><div>
</div></div><div><div><div>146</div></div><div><span>        </span><span>if</span><span> (</span><span>typeof</span><span> </span><span>finalResult</span><span> </span><span>===</span><span> </span><span>'undefined'</span><span>) </span><span>finalResult</span><span> </span><span>=</span><span> </span><span>result</span></div></div><div><div><div>147</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>148</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>149</div></div><div>
</div></div><div><div><div>150</div></div><div><span>    </span><span>return</span><span> </span><span>finalResult</span></div></div><div><div><div>151</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>152</div></div><div>
</div></div><div><div><div>153</div></div><div><span>  </span><span>resolveNames</span><span><span>(</span><span>_names</span><span>) {</span></span></div></div><div><div><div>154</div></div><div><span>    </span><span>let</span><span> </span><span>names</span><span> </span><span>=</span><span> </span><span>_names</span></div></div><div><div><div>155</div></div><div><span>    </span><span>names</span><span> </span><span>=</span><span><span> </span><span>names</span><span>.</span></span><span>replace</span><span>(</span><span>/</span><span>[</span><span>^</span><span><span>a-zA-Z0-9 ,/</span><span>.</span><span>]</span></span><span>/</span><span>g</span><span>, </span><span>''</span><span>)</span></div></div><div><div><div>156</div></div><div><span>    </span><span>names</span><span> </span><span>=</span><span><span> </span><span>names</span><span>.</span></span><span>replace</span><span>(</span><span>/</span><span>[,/]</span><span><span>+</span><span>/</span></span><span>g</span><span>, </span><span>' '</span><span>)</span></div></div><div><div><div>157</div></div><div><span>    </span><span>names</span><span> </span><span>=</span><span><span> </span><span>names</span><span>.</span></span><span>split</span><span>(</span><span>' '</span><span>)</span></div></div><div><div><div>158</div></div><div>
</div></div><div><div><div>159</div></div><div><span>    </span><span>return</span><span> </span><span>names</span></div></div><div><div><div>160</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>161</div></div><div>
</div></div><div><div><div>162</div></div><div><span>  </span><span>resolveName</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>163</div></div><div><span>    </span><span>const</span><span> </span><span>newName</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>164</div></div><div><span>    </span><span>const</span><span> </span><span>parts</span><span> </span><span>=</span><span><span> </span><span>name</span><span>.</span></span><span>split</span><span>(</span><span>'.'</span><span>)</span></div></div><div><div><div>165</div></div><div>
</div></div><div><div><div>166</div></div><div><span><span>    </span></span><span>newName</span><span>.</span><span>original</span><span> </span><span>=</span><span> </span><span>name</span></div></div><div><div><div>167</div></div><div><span><span>    </span></span><span>newName</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>parts</span><span>[</span><span>0</span><span>]</span></div></div><div><div><div>168</div></div><div><span><span>    </span></span><span>newName</span><span>.</span><span>namespace</span><span> </span><span>=</span><span> </span><span>'base'</span><span> </span><span>// Base namespace</span></div></div><div><div><div>169</div></div><div>
</div></div><div><div><div>170</div></div><div><span>    </span><span>// Specified namespace</span></div></div><div><div><div>171</div></div><div><span>    </span><span>if</span><span><span> (</span><span>parts</span><span>.</span></span><span>length</span><span> </span><span>&gt;</span><span> </span><span>1</span><span> </span><span>&amp;&amp;</span><span> </span><span>parts</span><span>[</span><span>1</span><span>] </span><span>!==</span><span> </span><span>''</span><span>) {</span></div></div><div><div><div>172</div></div><div><span><span>      </span></span><span>newName</span><span>.</span><span>namespace</span><span> </span><span>=</span><span> </span><span>parts</span><span>[</span><span>1</span><span>]</span></div></div><div><div><div>173</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>174</div></div><div>
</div></div><div><div><div>175</div></div><div><span>    </span><span>return</span><span> </span><span>newName</span></div></div><div><div><div>176</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>177</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h1>Three<a href="#three"><span>#</span></a></h1><section><h2>Camera Class<a href="#camera-class"><span>#</span></a></h2><p>创建 camera 和轨道控制时，需要提供 size 和 canvas，再 <code>camera class</code>无法直接访问这些属性。可以通过下面3种方法</p><ul>
<li>
<p>创建全局变量</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// Experience Class</span></div></div><div><div><div>2</div></div><div><span><span>window</span><span>.</span></span><span>experience</span><span> </span><span>=</span><span> </span><span>this</span></div></div><div><div><div>3</div></div><div><span>// Camera Class</span></div></div><div><div><div>4</div></div><div><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>experience</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>参数传递</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// Experience Class</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>camera</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Camera</span><span>(</span><span>this</span><span>)</span></div></div><div><div><div>3</div></div><div><span>// Camera Class</span></div></div><div><div><div>4</div></div><div><span>constructor</span><span><span>(</span><span>experience</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>  </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span><span> </span><span>experience</span></span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>使用单例(singleton)</p>
<p>单例类只有在第一次实例化时，会创建一个实例，若已经创建过实例对象，则会返回那个实例对象。</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// Experience Class</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>instance</span><span> </span></span><span>=</span><span> </span><span>null</span></div></div><div><div><div>3</div></div><div><span>// 单例模式，确保只有一个Experience实例存在</span></div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Experience</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>canvas</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (</span><span>instance</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>      </span><span>return</span><span> </span><span>instance</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>    </span><span>instance</span><span> </span><span>=</span><span> </span><span>this</span></div></div><div><div><div>10</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>11</div></div><div><span>    </span><span>this</span><span>.</span><span>camera</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Camera</span><span>()</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// Camera Class</span></div></div><div><div><div>16</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'./Experience'</span></div></div><div><div><div>17</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Camera</span><span> {</span></div></div><div><div><div>18</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>    </span><span>// Base camera</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul><p>创建 Camera Class，不单独监听resize事件，而是通过Experience类中的<code>sizes.on('resize')</code>来统一监听，防止在每个类中都调用，只监听一次</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>OrbitControls</span><span> } </span><span>from</span><span> </span><span>'three/examples/jsm/controls/OrbitControls.js'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'./Experience'</span></div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Camera</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>sizes</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>sizes</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>scene</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>canvas</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>canvas</span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>setInstance</span><span>()</span></div></div><div><div><div>11</div></div><div><span>    </span><span>this</span><span>.</span><span>setOrbitControls</span><span>()</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>setInstance</span><span>() {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>PerspectiveCamera</span><span>(</span><span>35</span><span>, </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>width</span><span> </span><span>/</span><span> </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>height</span><span>, </span><span>0.1</span><span>, </span><span>100</span><span>)</span></div></div><div><div><div>16</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>position</span><span>.</span><span>set</span><span>(</span><span>6</span><span>, </span><span>4</span><span>, </span><span>8</span><span>)</span></div></div><div><div><div>17</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>instance</span><span>)</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>  </span><span>setOrbitControls</span><span>() {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>controls</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>OrbitControls</span><span>(</span><span>this</span><span>.</span><span>instance</span><span>, </span><span>this</span><span>.</span><span>canvas</span><span>)</span></div></div><div><div><div>21</div></div><div><span>    </span><span>this</span><span>.</span><span>controls</span><span>.</span><span>enableDamping</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span>  </span><span>// resize 事件触发时调用，更新相机的宽高比</span></div></div><div><div><div>24</div></div><div><span>  </span><span>resize</span><span>() {</span></div></div><div><div><div>25</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>aspect</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>width</span><span> </span><span>/</span><span> </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>height</span></div></div><div><div><div>26</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>updateProjectionMatrix</span><span>()</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span>  </span><span>// tick 事件触发时调用，更新相机的控制器</span></div></div><div><div><div>29</div></div><div><span>  </span><span>update</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>controls</span><span>.</span><span>update</span><span>()</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Renderer Class<a href="#renderer-class"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'./Experience'</span></div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Renderer</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>canvas</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>canvas</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>sizes</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>sizes</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>scene</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>camera</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>camera</span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>setInstance</span><span>()</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>  </span><span>setInstance</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>WebGLRenderer</span><span>({</span></div></div><div><div><div>14</div></div><div><span>      </span><span>canvas</span><span>:</span><span> </span><span>this</span><span>.</span><span>canvas</span><span>,</span></div></div><div><div><div>15</div></div><div><span>      </span><span>antialias</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>17</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>toneMapping</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>CineonToneMapping</span></div></div><div><div><div>18</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>toneMappingExposure</span><span> </span><span>=</span><span> </span><span>1.75</span></div></div><div><div><div>19</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>shadowMap</span><span>.</span><span>enabled</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>shadowMap</span><span>.</span><span>type</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>PCFSoftShadowMap</span></div></div><div><div><div>21</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>setClearColor</span><span>(</span><span>'#211d20'</span><span>)</span></div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>setSize</span><span>(</span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>width</span><span>, </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>height</span><span>)</span></div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>setPixelRatio</span><span><span>(</span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>window</span><span>.</span></span><span>devicePixelRatio</span><span>, </span><span>2</span><span>))</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>  </span><span>resize</span><span>() {</span></div></div><div><div><div>26</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>setSize</span><span>(</span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>width</span><span>, </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>height</span><span>)</span></div></div><div><div><div>27</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>setPixelRatio</span><span><span>(</span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>window</span><span>.</span></span><span>devicePixelRatio</span><span>, </span><span>2</span><span>))</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>  </span><span>update</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>instance</span><span>.</span><span>render</span><span>(</span><span>this</span><span>.</span><span>scene</span><span>, </span><span>this</span><span>.</span><span>camera</span><span>.</span><span>instance</span><span>)</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h1>Wolrd<a href="#wolrd"><span>#</span></a></h1><section><h2>Wolrd Class<a href="#wolrd-class"><span>#</span></a></h2><p>Wolrd 类包含了，需要加载的lights，models，meshes等等都在这里实例化。自身在 <code>Experience Class</code>中实例化</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'../Experience.js'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>Environment</span><span> </span><span>from</span><span> </span><span>'./Environment.js'</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>Floor</span><span> </span><span>from</span><span> </span><span>'./Floor.js'</span></div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>Fox</span><span> </span><span>from</span><span> </span><span>'./Fox.js'</span></div></div><div><div><div>6</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>World</span><span> {</span></div></div><div><div><div>7</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>scene</span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>resources</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>resources</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 监听资源加载完成事件</span></div></div><div><div><div>13</div></div><div><span>    </span><span>this</span><span>.</span><span>resources</span><span>.</span><span>on</span><span>(</span><span>'ready'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span>      </span><span>this</span><span>.</span><span>floor</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Floor</span><span>()</span></div></div><div><div><div>15</div></div><div><span>      </span><span>this</span><span>.</span><span>fox</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Fox</span><span>()</span></div></div><div><div><div>16</div></div><div><span>      </span><span>// environmentMap 需要在最后执行，否则之后的还没加入sence导致添加不上</span></div></div><div><div><div>17</div></div><div><span>      </span><span>this</span><span>.</span><span>environment</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Environment</span><span>()</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>update</span><span>() {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>fox</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>      </span><span>this</span><span>.</span><span>fox</span><span>.</span><span>update</span><span>()</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Environment Class<a href="#environment-class"><span>#</span></a></h2><p>创建环境贴图，添加debug选项</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'../Experience.js'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Environment</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>scene</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>resources</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>resources</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>debug</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>debug</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>debug</span><span>.</span><span>ui</span><span>.</span><span>addFolder</span><span>(</span><span>'environment'</span><span>)</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>this</span><span>.</span><span>setSunlight</span><span>()</span></div></div><div><div><div>16</div></div><div><span>    </span><span>this</span><span>.</span><span>setEnvironmentMap</span><span>()</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>  </span><span>setSunlight</span><span>() {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>sunlight</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>DirectionalLight</span><span>(</span><span>'#ffffff'</span><span>, </span><span>4</span><span>)</span></div></div><div><div><div>21</div></div><div><span>    </span><span>this</span><span>.</span><span>sunlight</span><span>.</span><span>castShadow</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>sunlight</span><span>.</span><span>shadow</span><span>.</span><span>camera</span><span>.</span><span>far</span><span> </span><span>=</span><span> </span><span>15</span></div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>sunlight</span><span>.</span><span>shadow</span><span>.</span><span>mapSize</span><span>.</span><span>set</span><span>(</span><span>1024</span><span>, </span><span>1024</span><span>)</span></div></div><div><div><div>24</div></div><div><span>    </span><span>this</span><span>.</span><span>sunlight</span><span>.</span><span>shadow</span><span>.</span><span>normalBias</span><span> </span><span>=</span><span> </span><span>0.05</span></div></div><div><div><div>25</div></div><div><span>    </span><span>this</span><span>.</span><span>sunlight</span><span>.</span><span>position</span><span>.</span><span>set</span><span>(</span><span>3.5</span><span>, </span><span>2</span><span>, </span><span>-</span><span>1.25</span><span>)</span></div></div><div><div><div>26</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>sunlight</span><span>)</span></div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>28</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>sunlight</span><span>, </span><span>'intensity'</span><span>)</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>.</span><span>name</span><span>(</span><span>'sunlightIntensity'</span><span>)</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>.</span><span>min</span><span>(</span><span>0</span><span>)</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>.</span><span>max</span><span>(</span><span>10</span><span>)</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>.</span><span>step</span><span>(</span><span>0.001</span><span>)</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>sunlight</span><span>.</span><span>position</span><span>, </span><span>'x'</span><span>)</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>.</span><span>min</span><span>(</span><span>-</span><span>5</span><span>)</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>.</span><span>max</span><span>(</span><span>5</span><span>)</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>.</span><span>step</span><span>(</span><span>0.001</span><span>)</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>42</div></div><div><span>  </span><span>setEnvironmentMap</span><span>() {</span></div></div><div><div><div>43</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>44</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>intensity</span><span> </span><span>=</span><span> </span><span>0.4</span></div></div><div><div><div>45</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>texture</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resources</span><span>.</span><span>items</span><span>.</span><span>environmentMapTexture</span></div></div><div><div><div>46</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>texture</span><span>.</span><span>colorSpace</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>SRGBColorSpace</span></div></div><div><div><div>47</div></div><div><span>    </span><span>// this.scene.background = this.environmentMap.texture</span></div></div><div><div><div>48</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>environment</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>texture</span></div></div><div><div><div>49</div></div><div><span>    </span><span>// 给mesh添加环境贴图</span></div></div><div><div><div>50</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>updateMaterials</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>51</div></div><div><span>      </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>traverse</span><span><span>(</span><span>child</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>52</div></div><div><span>        </span><span>if</span><span> (</span></div></div><div><div><div>53</div></div><div><span>          </span><span>child</span><span> </span><span>instanceof</span><span> </span><span>THREE</span><span>.</span><span>Mesh</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>54</div></div><div><span><span>          </span></span><span>child</span><span>.</span><span>material</span><span> </span><span>instanceof</span><span> </span><span>THREE</span><span>.</span><span>MeshStandardMaterial</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>) {</span></div></div><div><div><div>56</div></div><div><span><span>          </span></span><span>child</span><span>.</span><span>material</span><span>.</span><span>envMap</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>texture</span></div></div><div><div><div>57</div></div><div><span><span>          </span></span><span>child</span><span>.</span><span>material</span><span>.</span><span>envMapIntensity</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>intensity</span></div></div><div><div><div>58</div></div><div><span><span>          </span></span><span>child</span><span>.</span><span>material</span><span>.</span><span>needsUpdate</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>59</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>60</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>62</div></div><div><span>    </span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>updateMaterials</span><span>()</span></div></div><div><div><div>63</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>64</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>environmentMap</span><span>, </span><span>'intensity'</span><span>)</span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>.</span><span>min</span><span>(</span><span>0</span><span>)</span></div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>.</span><span>max</span><span>(</span><span>4</span><span>)</span></div></div><div><div><div>68</div></div><div><span><span>        </span></span><span>.</span><span>step</span><span>(</span><span>0.001</span><span>)</span></div></div><div><div><div>69</div></div><div><span><span>        </span></span><span>.</span><span>onChange</span><span>(</span><span>this</span><span>.</span><span>environmentMap</span><span>.</span><span>updateMaterials</span><span>)</span></div></div><div><div><div>70</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>71</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>72</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Floor Class<a href="#floor-class"><span>#</span></a></h2><p>地板，一个<code>CircleGeometry</code>,用于接受阴影</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'../Experience.js'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Floor</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>scene</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>resources</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>resources</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>setGeometry</span><span>()</span></div></div><div><div><div>11</div></div><div><span>    </span><span>this</span><span>.</span><span>setTexture</span><span>()</span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>setMaterial</span><span>()</span></div></div><div><div><div>13</div></div><div><span>    </span><span>this</span><span>.</span><span>setMesh</span><span>()</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>setGeometry</span><span>() {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>this</span><span>.</span><span>geometry</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>CircleGeometry</span><span>(</span><span>5</span><span>, </span><span>64</span><span>)</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>  </span><span>setTexture</span><span>() {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>21</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>color</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resources</span><span>.</span><span>items</span><span>.</span><span>grassColorTexture</span></div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>color</span><span>.</span><span>repeat</span><span>.</span><span>set</span><span>(</span><span>1.5</span><span>, </span><span>1.5</span><span>)</span></div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>color</span><span>.</span><span>colorSpace</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>SRGBColorSpace</span></div></div><div><div><div>24</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>color</span><span>.</span><span>wrapS</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>RepeatWrapping</span></div></div><div><div><div>25</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>color</span><span>.</span><span>wrapT</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>RepeatWrapping</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>normal</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resources</span><span>.</span><span>items</span><span>.</span><span>grassNormalTexture</span></div></div><div><div><div>28</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>normal</span><span>.</span><span>repeat</span><span>.</span><span>set</span><span>(</span><span>1.5</span><span>, </span><span>1.5</span><span>)</span></div></div><div><div><div>29</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>normal</span><span>.</span><span>wrapS</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>RepeatWrapping</span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>normal</span><span>.</span><span>wrapT</span><span> </span><span>=</span><span> </span><span>THREE</span><span>.</span><span>RepeatWrapping</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>  </span><span>setMaterial</span><span>() {</span></div></div><div><div><div>33</div></div><div><span>    </span><span>this</span><span>.</span><span>material</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>MeshStandardMaterial</span><span>({</span></div></div><div><div><div>34</div></div><div><span>      </span><span>map</span><span>:</span><span> </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>color</span><span>,</span></div></div><div><div><div>35</div></div><div><span>      </span><span>normalMap</span><span>:</span><span> </span><span>this</span><span>.</span><span>texture</span><span>.</span><span>normal</span><span>,</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span>  </span><span>setMesh</span><span>() {</span></div></div><div><div><div>39</div></div><div><span>    </span><span>this</span><span>.</span><span>mesh</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>Mesh</span><span>(</span><span>this</span><span>.</span><span>geometry</span><span>, </span><span>this</span><span>.</span><span>material</span><span>)</span></div></div><div><div><div>40</div></div><div><span>    </span><span>this</span><span>.</span><span>mesh</span><span>.</span><span>rotation</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>-</span><span><span>Math</span><span>.</span></span><span>PI</span><span> </span><span>*</span><span> </span><span>0.5</span></div></div><div><div><div>41</div></div><div><span>    </span><span>this</span><span>.</span><span>mesh</span><span>.</span><span>receiveShadow</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>42</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>mesh</span><span>)</span></div></div><div><div><div>43</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>44</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>Fox Class<a href="#fox-class"><span>#</span></a></h2><p>加载 fox model ，添加动画和debug调试</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>THREE</span><span> </span><span>from</span><span> </span><span>'three'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Experience</span><span> </span><span>from</span><span> </span><span>'../Experience.js'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>class</span><span> </span><span>Fox</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>experience</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Experience</span><span>()</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>sence</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>scene</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>resources</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>resources</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>time</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>time</span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>debug</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>experience</span><span>.</span><span>debug</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// Debug</span></div></div><div><div><div>13</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>debug</span><span>.</span><span>ui</span><span>.</span><span>addFolder</span><span>(</span><span>'fox'</span><span>)</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// model</span></div></div><div><div><div>18</div></div><div><span>    </span><span>this</span><span>.</span><span>resource</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resources</span><span>.</span><span>items</span><span>.</span><span>foxModel</span></div></div><div><div><div>19</div></div><div><span>    </span><span>this</span><span>.</span><span>setModel</span><span>()</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>setAnimation</span><span>()</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>  </span><span>setModel</span><span>() {</span></div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>model</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>resource</span><span>.</span><span>scene</span></div></div><div><div><div>24</div></div><div><span>    </span><span>this</span><span>.</span><span>model</span><span>.</span><span>scale</span><span>.</span><span>set</span><span>(</span><span>0.02</span><span>, </span><span>0.02</span><span>, </span><span>0.02</span><span>)</span></div></div><div><div><div>25</div></div><div><span>    </span><span>this</span><span>.</span><span>sence</span><span>.</span><span>add</span><span>(</span><span>this</span><span>.</span><span>model</span><span>)</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>// 给模型添加阴影</span></div></div><div><div><div>28</div></div><div><span>    </span><span>this</span><span>.</span><span>model</span><span>.</span><span>traverse</span><span><span>(</span><span>child</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>29</div></div><div><span>      </span><span>if</span><span> (</span><span>child</span><span> </span><span>instanceof</span><span> </span><span>THREE</span><span>.</span><span>Mesh</span><span>) {</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>child</span><span>.</span><span>castShadow</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span>  </span><span>setAnimation</span><span>() {</span></div></div><div><div><div>35</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>36</div></div><div><span>    </span><span>// 获取动画</span></div></div><div><div><div>37</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>mixer</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>THREE</span><span>.</span><span>AnimationMixer</span><span>(</span><span>this</span><span>.</span><span>model</span><span>)</span></div></div><div><div><div>38</div></div><div><span>    </span><span>// 动作</span></div></div><div><div><div>39</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>40</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>idle</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>mixer</span><span>.</span><span>clipAction</span><span>(</span></div></div><div><div><div>41</div></div><div><span>      </span><span>this</span><span>.</span><span>resource</span><span>.</span><span>animations</span><span>[</span><span>0</span><span>]</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>43</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>walking</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>mixer</span><span>.</span><span>clipAction</span><span>(</span></div></div><div><div><div>44</div></div><div><span>      </span><span>this</span><span>.</span><span>resource</span><span>.</span><span>animations</span><span>[</span><span>1</span><span>]</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>46</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>running</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>mixer</span><span>.</span><span>clipAction</span><span>(</span></div></div><div><div><div>47</div></div><div><span>      </span><span>this</span><span>.</span><span>resource</span><span>.</span><span>animations</span><span>[</span><span>2</span><span>]</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>current</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>idle</span></div></div><div><div><div>51</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>current</span><span>.</span><span>play</span><span>()</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>// 切换动画的过渡</span></div></div><div><div><div>54</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>play</span><span> </span><span>=</span><span><span> </span><span>name</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>55</div></div><div><span>      </span><span>const</span><span> </span><span>newAction</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>[</span><span>name</span><span>]</span></div></div><div><div><div>56</div></div><div><span>      </span><span>const</span><span> </span><span>oldAction</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>current</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span><span>      </span></span><span>newAction</span><span>.</span><span>reset</span><span>()</span></div></div><div><div><div>59</div></div><div><span><span>      </span></span><span>newAction</span><span>.</span><span>play</span><span>()</span></div></div><div><div><div>60</div></div><div><span><span>      </span></span><span>newAction</span><span>.</span><span>crossFadeFrom</span><span>(</span><span>oldAction</span><span>, </span><span>1</span><span>)</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>      </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>actions</span><span>.</span><span>current</span><span> </span><span>=</span><span> </span><span>newAction</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>64</div></div><div><span>    </span><span>// 添加debug选项</span></div></div><div><div><div>65</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) {</span></div></div><div><div><div>66</div></div><div><span>      </span><span>const</span><span> </span><span>debugObject</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>67</div></div><div><span>        </span><span>playIdle</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>play</span><span>(</span><span>'idle'</span><span>),</span></div></div><div><div><div>68</div></div><div><span>        </span><span>playWalking</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>play</span><span>(</span><span>'walking'</span><span>),</span></div></div><div><div><div>69</div></div><div><span>        </span><span>playRunning</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>play</span><span>(</span><span>'running'</span><span>),</span></div></div><div><div><div>70</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>71</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span><span>.</span><span>add</span><span>(</span><span>debugObject</span><span>, </span><span>'playIdle'</span><span>)</span></div></div><div><div><div>72</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span><span>.</span><span>add</span><span>(</span><span>debugObject</span><span>, </span><span>'playWalking'</span><span>)</span></div></div><div><div><div>73</div></div><div><span>      </span><span>this</span><span>.</span><span>debugFolder</span><span>.</span><span>add</span><span>(</span><span>debugObject</span><span>, </span><span>'playRunning'</span><span>)</span></div></div><div><div><div>74</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>// this.animation.action = this.animation.mixer.clipAction(</span></div></div><div><div><div>77</div></div><div><span>    </span><span>//   this.resource.animations[0]</span></div></div><div><div><div>78</div></div><div><span>    </span><span>// )</span></div></div><div><div><div>79</div></div><div><span>    </span><span>// this.animation.action.play()</span></div></div><div><div><div>80</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>81</div></div><div><span>  </span><span>// 每帧更新动画</span></div></div><div><div><div>82</div></div><div><span>  </span><span>// update方法会在Experience Class的update方法中被调用</span></div></div><div><div><div>83</div></div><div><span>  </span><span>update</span><span>() {</span></div></div><div><div><div>84</div></div><div><span>    </span><span>this</span><span>.</span><span>animation</span><span>.</span><span>mixer</span><span>.</span><span>update</span><span>(</span><span>this</span><span>.</span><span>time</span><span>.</span><span>delta</span><span> </span><span>*</span><span> </span><span>0.001</span><span>)</span></div></div><div><div><div>85</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>86</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h1>Destroy<a href="#destroy"><span>#</span></a></h1><p>首先移除事件监听，因为通知事件是继承 <code>EventEmitter Class</code>，所以直接调用 <code>off</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>this</span><span>.</span><span>sizes</span><span>.</span><span>off</span><span>(</span><span>'resize'</span><span>)</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>time</span><span>.</span><span>off</span><span>(</span><span>'tick'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>移除<code>sence</code>中的对象，通过遍历整个<code>sence</code>，查找需要处理的对象。<a href="https://threejs.org/manual/?q=dispose#en/how-to-dispose-of-objects" target="_blank">how to dispose of object</a> 主要移除geometry，material，texture，control等等</p><p>如果是<code>Mesh</code>,直接调用<code>dispose()</code>方法，遍历<code>material</code>如果有<code>dispose()</code>就调用</p><p>最后卸载 <code>camera</code> <code>renderer</code> <code>debug</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>destroy</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>// EventEmitter的方法，移除事件监听</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>sizes</span><span>.</span><span>off</span><span>(</span><span>'resize'</span><span>)</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>time</span><span>.</span><span>off</span><span>(</span><span>'tick'</span><span>)</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>// 遍历场景中的所有物体</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>scene</span><span>.</span><span>traverse</span><span><span>(</span><span>child</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>      </span><span>// 检查物体是否是Mesh</span></div></div><div><div><div>9</div></div><div><span>      </span><span>if</span><span><span> (</span><span>child</span><span> </span></span><span>instanceof</span><span> </span><span>THREE</span><span>.</span><span>Mesh</span><span>) {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>child</span><span>.</span><span>geometry</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>        </span><span>// 遍历网格模型的材质，调用dispose方法</span></div></div><div><div><div>13</div></div><div><span>        </span><span>for</span><span> (</span><span>const</span><span> </span><span>key</span><span> </span><span>in</span><span><span> </span><span>child</span><span>.</span></span><span>material</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>          </span><span>const</span><span> </span><span>value</span><span> </span><span>=</span><span><span> </span><span>child</span><span>.</span></span><span>material</span><span><span>[</span><span>key</span><span>]</span></span></div></div><div><div><div>15</div></div><div><span>          </span><span>if</span><span><span> (</span><span>value</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>typeof</span><span><span> </span><span>value</span><span>.</span></span><span>dispose</span><span> </span><span>===</span><span> </span><span>'function'</span><span>) {</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>value</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>21</div></div><div><span>    </span><span>this</span><span>.</span><span>camera</span><span>.</span><span>controls</span><span>.</span><span>dispose</span><span>()</span></div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>renderer</span><span>.</span><span>instance</span><span>.</span><span>dispose</span><span>()、</span></div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>.</span><span>active</span><span>) </span><span>this</span><span>.</span><span>debug</span><span>.</span><span>ui</span><span>.</span><span>destroy</span><span>()</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/ru-he-yi-chu-github-zhong-de-contributors/</id>
      <title type="text">如何移除 GitHub 中的 contributors</title>
      <published>2025-04-06T06:57:08.000Z</published>
      <updated>2025-04-06T06:57:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/ru-he-yi-chu-github-zhong-de-contributors/"/>
      <summary type="text">如果你在某次 commit 时，忘记 git config 设置自己的 username 和 email 了，发现仓库主页 contributors，多了一个contributor，那么如何才能移除呢？ 虽然官方没有移除contributor的办法，但是我们可以替换掉所有的username和emai…</summary>
      <content type="html"><![CDATA[<p>如果你在某次 commit 时，忘记 <code>git config</code> 设置自己的 <code>username</code> 和 <code>email</code>了，发现仓库主页 contributors，多了一个contributor，那么如何才能移除呢？</p>
<p>虽然官方没有移除contributor的办法，但是我们可以替换掉所有的username和email啊 这样全都是我们提交的了</p>
<p>通过 <code>git filter-branch</code> Git 内置的重写历史工具，允许批量修改提交记录。</p>
<p><code>--commit-filter</code>指定一个自定义脚本，针对每个提交执行操作。脚本的返回值会替换原始提交。</p>
<p>将作者信息替换为新的姓名和邮箱</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>filter-branch</span><span> </span><span>--commit-filter</span><span> </span><span>'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if [ "$GIT_AUTHOR_EMAIL" = "old email@example.com" ];</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>then</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>GIT_AUTHOR_NAME="new username";</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>GIT_AUTHOR_EMAIL="new email@example.com";</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>git commit-tree "$@";</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>else</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>git commit-tree "$@";</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>fi'</span><span> </span><span>HEAD</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>再将提交者信息替换为新的姓名和邮箱</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>filter-branch</span><span> </span><span>--commit-filter</span><span> </span><span>'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if [ "$GIT_COMMITTER_EMAIL" = "old email@example.com" ];</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>then</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>GIT_COMMITTER_NAME="new username";</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>GIT_COMMITTER_EMAIL="new email@example.com";</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>git commit-tree "$@";</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>else</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>git commit-tree "$@";</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>fi'</span><span> </span><span>HEAD</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>这两个命令的作用是<strong>重写Git提交历史，将特定作者邮箱的提交修改为新的作者姓名和邮箱</strong>。</p>
<p>当然官方推荐使用更安全的替代工具 <code>git filter-repo</code>。</p>
<p>至此commit记录里面的作者改掉了，可是那该死的主页上还有contributor怎么办？</p>
<p>我们可以拷贝一个版本库，在新的仓库里面提交。</p>
<ol>
<li>在 GitHub 上创建一个全新的仓库，并且创建一个<code>main</code> 分支</li>
<li>将原来版本库重命名，并且将内容拷贝到新的仓库</li>
<li>新的仓库就是完完全全修改了git记录的仓库了</li>
</ol>
<p>怎么样很简单吧</p>
<p>建立好代码备份后，在本地仓库添加新创建的远程仓库，移除掉原来旧的远程仓库，拉取新的仓库，最后把本地强制覆盖远程仓库</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>remote</span><span> </span><span>add</span><span> </span><span>localrepository</span><span> </span><span>https://github.com/your-account/new-repository.git</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>remote</span><span> </span><span>remove</span><span> </span><span>origin</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>pull</span><span> </span><span>localrepository</span></div></div><div><div><div>4</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>--force-with-lease</span><span> </span><span>localrepository</span><span> </span><span>main</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>最后，再将新的仓库 <code>git clone</code> 下来开发。</p>
<p>至此，新的仓库保留了所有的git记录，并且username和email也改掉了，新仓库首页也没有该死的contributors了，めでたいめでたい。至于旧的祝他幸福。</p>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vuex/</id>
      <title type="text">Vuex</title>
      <published>2022-10-08T02:21:45.000Z</published>
      <updated>2022-10-08T02:21:45.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vuex/"/>
      <summary type="text">Vuex 是在Vue中实现集中式状态（数据）管理的一个Vue插件，对vue应用中多个组件的共享状态进行集中式的管理（读/写），也是一种组件间通信的方式，且适用于任意组件间通信。</summary>
      <content type="html"><![CDATA[<p>Vuex 是在Vue中实现集中式状态（数据）管理的一个Vue插件，对vue应用中多个组件的共享状态进行集中式的管理（读/写），也是一种组件间通信的方式，且适用于任意组件间通信。</p>
<section><h2>Vuex 理解<a href="#vuex-理解"><span>#</span></a></h2><p><strong>Vuex 是一个全局的共享存储区域，相当于是一个数据仓库</strong></p><p>Vuex 是为了保存组件之间的共享数据而诞生的，如果组件之间要有共享数据，可以直接挂载到 Vuex 中，而不必通过父子组件直接的传值了，而私有的数据则不需要挂载到 Vuex 中，<strong>只有需要共享的数据才放在 Vuex 中，私有的数据只需要放在组件的 data 中即可</strong></p><section><h3>多组件共享状态的问题<a href="#多组件共享状态的问题"><span>#</span></a></h3><ol>
<li>
<p>多个视图依赖于同一状态</p>
</li>
<li>
<p>来自不同视图的行为需要变更同一状态</p>
</li>
<li>
<p>以前的解决办法：</p>
<p>a. 将数据以及操作数据的行为都定义在父组件</p>
<p>b. 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)</p>
</li>
</ol><p>vuex 就是用来解决这个问题的</p></section><section><h3>状态自管理应用<a href="#状态自管理应用"><span>#</span></a></h3><p>这个状态自管理应用包含以下几个部分：</p><ol>
<li><code>state</code>: 驱动应用的数据源</li>
<li><code>view</code>: 以声明方式将 <code>state</code> 映射到视图</li>
<li><code>actions</code>: 响应在 <code>view</code> 上的用户输入导致的状态变化(包含 n 个更新状态的方法)</li>
</ol><p>以下是一个表示“单向数据流”理念的简单示意：</p><p></p><figure><img src="/images/vue/vue_Vuex_%E7%8A%B6%E6%80%81%E8%87%AA%E7%AE%A1%E7%90%86%E5%BA%94%E7%94%A8.png" alt="vue_Vuex_状态自管理应用" /><figcaption>vue_Vuex_状态自管理应用</figcaption></figure><p></p></section><section><h3>Vuex 的结构模型<a href="#vuex-的结构模型"><span>#</span></a></h3><p></p><figure><img src="/images/vue/vue_Vuex_%E7%BB%93%E6%9E%84%E6%A8%A1%E5%9E%8B.png" alt="vue_Vuex_结构模型" /><figcaption>vue_Vuex_结构模型</figcaption></figure><p></p></section></section>
<section><h2>搭建vuex环境<a href="#搭建vuex环境"><span>#</span></a></h2><ol>
<li>
<p>下载<code>vuex</code>插件：<code>npm i vuex</code></p>
<p>注：<code>vue2</code>环境中不支持 <code>vuex4</code>，所以在 <code>vue2</code> 中应该使用 <code>npm i vuex@3</code> 命令安装 <code>vuex3</code></p>
</li>
<li>
<p>创建文件：<code>src/store/index.js</code></p>
</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//引入Vue核心库</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>3</div></div><div><span>//引入Vuex</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>Vuex</span><span> </span><span>from</span><span> </span><span>'vuex'</span></div></div><div><div><div>5</div></div><div><span>//应用Vuex插件</span></div></div><div><div><div>6</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>Vuex</span><span>)</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>//准备actions对象——响应组件中用户的动作</span></div></div><div><div><div>9</div></div><div><span>const</span><span> </span><span>actions</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>10</div></div><div><span>//准备mutations对象——修改state中的数据</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>mutations</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>12</div></div><div><span>//准备state对象——保存具体的数据</span></div></div><div><div><div>13</div></div><div><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>//创建并暴露store</span></div></div><div><div><div>16</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>new</span><span><span> </span><span>Vuex</span><span>.</span></span><span>Store</span><span>({</span></div></div><div><div><div>17</div></div><div><span>  </span><span>actions</span><span>,</span></div></div><div><div><div>18</div></div><div><span>  </span><span>mutations</span><span>,</span></div></div><div><div><div>19</div></div><div><span>  </span><span>state</span></div></div><div><div><div>20</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>在<code>main.js</code>中创建<code>vm</code>时传入<code>store</code>配置项</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//引入store</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>store</span><span> </span><span>from</span><span> </span><span>'./store'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>//创建vm</span></div></div><div><div><div>5</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>6</div></div><div><span>  </span><span>el</span><span>:</span><span>'#app'</span><span>,</span></div></div><div><div><div>7</div></div><div><span>  </span><span>render</span><span>:</span><span><span> </span><span>h</span><span> </span></span><span>=&gt;</span><span> </span><span>h</span><span><span>(</span><span>App</span><span>),</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>store</span><span>,</span></div></div><div><div><div>9</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>项目结构<a href="#项目结构"><span>#</span></a></h2><p><code>Vuex</code> 并不限制你的代码结构。但是，它规定了一些需要遵守的规则：</p><ol>
<li>应用层级的状态应该集中到单个 <code>store</code> 对象中。</li>
<li>提交 <code>mutation</code> 是更改状态的唯一方法，并且这个过程是同步的。</li>
<li>异步逻辑都应该封装到 <code>action</code> 里面。</li>
</ol><p>只要你遵守以上规则，如何组织代码随你便。如果你的 <code>store</code> 文件太大，只需将 <code>action</code>、<code>mutation</code> 和 <code>getter</code> 分割到单独的文件。</p><p>对于大型应用，我们会希望把 <code>Vuex</code> 相关代码分割到模块中。下面是项目结构示例：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>├──</span><span> </span><span>index.html</span></div></div><div><div><div>2</div></div><div><span>├──</span><span> </span><span>main.js</span></div></div><div><div><div>3</div></div><div><span>├──</span><span> </span><span>api</span></div></div><div><div><div>4</div></div><div><span>│</span><span>   </span><span>└──</span><span> </span><span>...</span><span> </span><span># 抽取出API请求</span></div></div><div><div><div>5</div></div><div><span>├──</span><span> </span><span>components</span></div></div><div><div><div>6</div></div><div><span>│</span><span>   </span><span>├──</span><span> </span><span>App.vue</span></div></div><div><div><div>7</div></div><div><span>│</span><span>   </span><span>└──</span><span> </span><span>...</span></div></div><div><div><div>8</div></div><div><span>└──</span><span> </span><span>store</span></div></div><div><div><div>9</div></div><div><span>    </span><span>├──</span><span> </span><span>index.js</span><span>          </span><span># 我们组装模块并导出 store 的地方</span></div></div><div><div><div>10</div></div><div><span>    </span><span>├──</span><span> </span><span>actions.js</span><span>        </span><span># 根级别的 action</span></div></div><div><div><div>11</div></div><div><span>    </span><span>├──</span><span> </span><span>mutations.js</span><span>      </span><span># 根级别的 mutation</span></div></div><div><div><div>12</div></div><div><span>    </span><span>└──</span><span> </span><span>modules</span></div></div><div><div><div>13</div></div><div><span>        </span><span>├──</span><span> </span><span>cart.js</span><span>       </span><span># 购物车模块</span></div></div><div><div><div>14</div></div><div><span>        </span><span>└──</span><span> </span><span>products.js</span><span>   </span><span># 产品模块</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>核心概念<a href="#核心概念"><span>#</span></a></h2><blockquote><p>vuex中一共有五个状态 State Getter Mutation  Action  Module</p></blockquote><section><h3>State<a href="#state"><span>#</span></a></h3><ol>
<li>
<p><code>state</code> 提供唯一的公共数据源，所有共享的数据统一放到<code>store</code>的<code>state</code>进行储存，类似于 <code>Vue</code> 中的 <code>data</code>属性</p>
</li>
<li>
<p>在<code>vuex</code>中<code>state</code>中定义数据，可以在任何组件中进行调用</p>
</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Vuex</span><span> </span><span>from</span><span> </span><span>'vuex'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>Vuex</span><span>)</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>new</span><span><span> </span><span>Vuex</span><span>.</span></span><span>Store</span><span>({</span></div></div><div><div><div>7</div></div><div><span>    </span><span>//数据，相当于data</span></div></div><div><div><div>8</div></div><div><span>    </span><span>state</span><span>:</span><span> {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'张三'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>        </span><span>age</span><span>:</span><span> </span><span>12</span><span>,</span></div></div><div><div><div>11</div></div><div><span>        </span><span>sum</span><span>:</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>使用：</li>
</ol><p>在标签中直接使用</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>div</span><span>&gt;{{ $store.state.name }}&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>div</span><span>&gt;{{ $store.state.age }}&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>组件中使用</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>state</span><span>.</span><span>name</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>state</span><span>.</span><span>age</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Mutations<a href="#mutations"><span>#</span></a></h3><ol>
<li>包含多个直接更新 <code>state</code> 的方法(回调函数)的对象</li>
<li>谁来触发: <code>action 中的 commit('mutationName')</code></li>
<li>只能包含同步的代码, 不能写异步代码</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span> </span><span>const</span><span> </span><span>mutations</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>mutationName</span><span><span> (</span><span>state</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>// 更新 state 某个属性的操作</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span><span>第一个参数</span><span>：</span><span>state</span><span> </span><span>等同于</span><span> </span><span>store</span><span>.</span></span><span>state</span><span><span> ，</span><span>若在模块中则为局部状态</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>第二个参数</span><span>：</span><span>value</span><span> </span><span>可以为任意类型</span><span>，</span><span>但最后都会包装成一个对象</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>以加法、减法操作为例，<code>state.sum</code> 加上或减去 <code>value</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>mutations</span><span>: {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>JIA</span><span><span>(</span><span>state</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>state</span><span>.</span><span>sum</span><span> </span><span>+=</span><span><span> </span><span>value</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>    </span><span>JIAN</span><span><span>(</span><span>state</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>state</span><span>.</span><span>sum</span><span> </span><span>-=</span><span><span> </span><span>value</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span>},</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Actions<a href="#actions"><span>#</span></a></h3><ol>
<li>
<p>包含多个事件回调函数的对象</p>
</li>
<li>
<p>通过执行: <code>commit()</code>来触发 <code>mutation</code> 的调用，间接更新 <code>state</code></p>
</li>
<li>
<p>谁来触发: 组件中: <code>this.$store.dispatch('actionName', value) value为参数</code></p>
</li>
<li>
<p>可以包含异步代码（如定时器，ajax）</p>
</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>actions</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>actionName</span><span><span> (</span><span>context</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>commit</span><span>(</span><span>'对应的mutationsName'</span><span><span>, </span><span>value</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>第一个参数</span><span>：</span><span>context对象</span></span></div></div><div><div><div>7</div></div><div><span><span>包含</span><span>：{</span></span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>state</span><span>,      </span><span>// 等同于 `store.state`，若在模块中则为局部状态</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>rootState</span><span>,  </span><span>// 等同于 `store.state`，只存在于模块中</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>commit</span><span>,     </span><span>// 等同于 `store.commit`</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>dispatch</span><span>,   </span><span>// 等同于 `store.dispatch`</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>getters</span><span>,    </span><span>// 等同于 `store.getters`</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>rootGetters</span><span> </span><span>// 等同于 `store.getters`，只存在于模块中</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div><span><span>第二个参数</span><span>： </span><span>可以为任意类型</span><span>，</span><span>但最后都会包装成一个对象</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>以 0.5s 后如果<code>sum</code>为奇数则加的加法操作为例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>actions</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>jiaWait</span><span><span>(</span><span>context</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>            </span><span>if</span><span><span> (</span><span>context</span><span>.</span></span><span>state</span><span>.</span><span>sum</span><span> </span><span>%</span><span> </span><span>2</span><span> </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>5</div></div><div><span><span>              </span></span><span>context</span><span>.</span><span>commit</span><span>(</span><span>'JIA'</span><span><span>, </span><span>value</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>}, </span><span>500</span><span>)</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span>},</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Getters<a href="#getters"><span>#</span></a></h3><ol>
<li>包含多个计算属性(<code>get</code>)的对象</li>
<li>当<code>state</code>中的数据需要经过加工后再使用时，可以使用<code>getters</code>加工，类似于 <code>Vue</code> 中的 <code>computed</code>属性</li>
<li>谁来读取: 组件中: <code>$store.getters.xxx</code></li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>getters</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>getterName</span><span><span>(</span><span>state</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>以将 <code>sum</code>乘 10 为例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>getters</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>bigSum</span><span><span>(</span><span>state</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span><span> </span><span>state</span><span>.</span></span><span>sum</span><span> </span><span>*</span><span> </span><span>10</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>},</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Modules<a href="#modules"><span>#</span></a></h3><ol>
<li>包含多个 <code>module</code></li>
<li>一个 <code>module</code> 是一个 <code>store</code> 的配置对象，可分别写在不同 <code>js</code>文件中，然后暴露即可，但是需要加上<code>namespaced: true,</code></li>
<li>与一个组件(包含有共享数据)对应</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Vuex</span><span> </span><span>from</span><span> </span><span>'vuex'</span></div></div><div><div><div>3</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>Vuex</span><span>)</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>moduleA</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>namespaced</span><span>:</span><span>true</span><span>,</span><span>//开启命名空间</span></div></div><div><div><div>7</div></div><div><span>    </span><span>state</span><span>:</span><span> {},</span></div></div><div><div><div>8</div></div><div><span>    </span><span>mutations</span><span>:</span><span> {},</span></div></div><div><div><div>9</div></div><div><span>    </span><span>actions</span><span>:</span><span> {},</span></div></div><div><div><div>10</div></div><div><span>    </span><span>getters</span><span>:</span><span> {},</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div><span>const</span><span> </span><span>moduleB</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>namespaced</span><span>:</span><span>true</span><span>,</span><span>//开启命名空间</span></div></div><div><div><div>14</div></div><div><span>    </span><span>state</span><span>:</span><span> {},</span></div></div><div><div><div>15</div></div><div><span>    </span><span>mutations</span><span>:</span><span> {},</span></div></div><div><div><div>16</div></div><div><span>    </span><span>actions</span><span>:</span><span> {},</span></div></div><div><div><div>17</div></div><div><span>    </span><span>getters</span><span>:</span><span> {},</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div><span>const</span><span> </span><span>store</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>Vuex</span><span>.</span></span><span>Store</span><span>({</span></div></div><div><div><div>20</div></div><div><span>    </span><span>modules</span><span>:</span><span> {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>moduleA</span><span>:</span><span><span> </span><span>moduleA</span><span>,</span></span></div></div><div><div><div>22</div></div><div><span>        </span><span>moduleB</span><span>:</span><span><span> </span><span>moduleB</span><span>,</span></span></div></div><div><div><div>23</div></div><div><span>        </span><span>// ES6 简写</span></div></div><div><div><div>24</div></div><div><span>        </span><span>// moduleA,</span></div></div><div><div><div>25</div></div><div><span>        </span><span>// moduleB,</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>27</div></div><div><span>})</span></div></div><div><div><div>28</div></div><div><span><span>store</span><span>.</span></span><span>state</span><span>.</span><span>moduleA</span><span> </span><span>// -&gt; moduleA 的状态</span></div></div><div><div><div>29</div></div><div><span><span>store</span><span>.</span></span><span>state</span><span>.</span><span>moduleB</span><span> </span><span>// -&gt; moduleB 的状态</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>四个<code>map</code>方法的使用<a href="#四个map方法的使用"><span>#</span></a></h2><p>导入</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> {</span><span>mapState</span><span>, </span><span>mapGetters</span><span>, </span><span>mapActions</span><span>, </span><span>mapMutations</span><span>} </span><span>from</span><span> </span><span>'vuex'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3><code>mapState</code>方法：<a href="#mapstate方法"><span>#</span></a></h3><p>用于帮助我们映射<code>state</code>中的数据为计算属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>computed</span><span>: {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>//借助mapState生成计算属性：sum、school、subject（对象写法）</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>...</span><span>mapState</span><span>({</span><span>sum</span><span>:</span><span>'sum'</span><span>,</span><span>school</span><span>:</span><span>'school'</span><span>,</span><span>subject</span><span>:</span><span>'subject'</span><span>}),</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>//借助mapState生成计算属性：sum、school、subject（数组写法）</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>...</span><span>mapState</span><span>([</span><span>'sum'</span><span>,</span><span>'school'</span><span>,</span><span>'subject'</span><span>]),</span></div></div><div><div><div>7</div></div><div><span>},</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>mapGetters</code>方法：<a href="#mapgetters方法"><span>#</span></a></h3><p>用于帮助我们映射<code>getters</code>中的数据为计算属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>computed</span><span>: {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>//借助mapGetters生成计算属性：bigSum（对象写法）</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>...</span><span>mapGetters</span><span>({</span><span>bigSum</span><span>:</span><span>'bigSum'</span><span>}),</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>//借助mapGetters生成计算属性：bigSum（数组写法）</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>...</span><span>mapGetters</span><span>([</span><span>'bigSum'</span><span>])</span></div></div><div><div><div>7</div></div><div><span>},</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>mapActions</code>方法：<a href="#mapactions方法"><span>#</span></a></h3><p>用于帮助我们生成与<code>actions</code>对话的方法，即：包含<code>$store.dispatch(xxx)</code>的函数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>methods</span><span>:{</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>//靠mapActions生成：incrementOdd、incrementWait（对象形式）</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>...</span><span>mapActions</span><span>({</span><span>incrementOdd</span><span>:</span><span>'jiaOdd'</span><span>,</span><span>incrementWait</span><span>:</span><span>'jiaWait'</span><span>})</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>//靠mapActions生成：incrementOdd、incrementWait（数组形式）</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>...</span><span>mapActions</span><span>([</span><span>'jiaOdd'</span><span>,</span><span>'jiaWait'</span><span>])</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>mapMutations</code>方法：<a href="#mapmutations方法"><span>#</span></a></h3><p>用于帮助我们生成与<code>mutations</code>对话的方法，即：包含<code>$store.commit(xxx)</code>的函数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>methods</span><span>:{</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>//靠mapActions生成：increment、decrement（对象形式）</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>...</span><span>mapMutations</span><span>({</span><span>increment</span><span>:</span><span>'JIA'</span><span>,</span><span>decrement</span><span>:</span><span>'JIAN'</span><span>}),</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>//靠mapMutations生成：JIA、JIAN（对象形式）</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>...</span><span>mapMutations</span><span>([</span><span>'JIA'</span><span>,</span><span>'JIAN'</span><span>]),</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>备注：mapActions与mapMutations使用时，若需要传递参数需要：在模板中绑定事件时传递好参数，否则传的参数是事件对象(event)。</p></blockquote><p>具体案例：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;</span><span>h1</span><span>&gt;当前求和为：{{ sum }}&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;</span><span>h3</span><span>&gt;当前求和放大10倍为：{{ bigSum }}&lt;/</span><span>h3</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;</span><span>h3</span><span>&gt;年龄：{{ age }}&lt;/</span><span>h3</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>&lt;</span><span>h3</span><span>&gt;姓名：{{ name }}&lt;/</span><span>h3</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>&lt;</span><span>select</span><span> </span><span>v-model.number</span><span>=</span><span>"n"</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>&lt;</span><span>option</span><span> </span><span>value</span><span>=</span><span>"1"</span><span>&gt;1&lt;/</span><span>option</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>&lt;</span><span>option</span><span> </span><span>value</span><span>=</span><span>"2"</span><span>&gt;2&lt;/</span><span>option</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>&lt;</span><span>option</span><span> </span><span>value</span><span>=</span><span>"3"</span><span>&gt;3&lt;/</span><span>option</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>&lt;/</span><span>select</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span>        </span><span>&lt;!-- 用了mapActions 和 mapMutations 的话要主动传参 --&gt;</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"increment(n)"</span><span>&gt;+&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"decrement(n)"</span><span>&gt;-&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"incrementOdd(n)"</span><span>&gt;当前求和为奇数再加&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"incrementWait(n)"</span><span>&gt;等0.5s再加&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>import</span><span> { </span><span>mapState</span><span>, </span><span>mapGetters</span><span>, </span><span>mapActions</span><span>, </span><span>mapMutations</span><span> } </span><span>from</span><span> </span><span>'vuex'</span></div></div><div><div><div>22</div></div><div><span>    </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'Count'</span><span>,</span></div></div><div><div><div>24</div></div><div><span>        </span><span>data</span><span>() {</span></div></div><div><div><div>25</div></div><div><span>            </span><span>return</span><span> {</span></div></div><div><div><div>26</div></div><div><span>                </span><span>n</span><span>:</span><span> </span><span>1</span><span>, </span><span>//用户选择的数字</span></div></div><div><div><div>27</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>29</div></div><div><span>        </span><span>computed</span><span>:</span><span> {</span></div></div><div><div><div>30</div></div><div><span><span>            </span></span><span>...</span><span>mapState</span><span>([</span><span>'sum'</span><span>, </span><span>'age'</span><span>, </span><span>'name'</span><span>]),</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>...</span><span>mapGetters</span><span>([</span><span>'bigSum'</span><span>]),</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>33</div></div><div><span>        </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>...</span><span>mapActions</span><span>({ </span><span>incrementOdd</span><span>:</span><span> </span><span>'sumOdd'</span><span>, </span><span>incrementWait</span><span>:</span><span> </span><span>'sumWait'</span><span> }),</span></div></div><div><div><div>35</div></div><div><span><span>            </span></span><span>...</span><span>mapMutations</span><span>({ </span><span>increment</span><span>:</span><span> </span><span>'sum'</span><span>, </span><span>decrement</span><span>:</span><span> </span><span>'reduce'</span><span> }),</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>37</div></div><div><span>        </span><span>mounted</span><span>() {</span></div></div><div><div><div>38</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Count'</span><span>, </span><span>this</span><span>)</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>模块化<a href="#模块化"><span>#</span></a></h2><p>目的：让代码更好维护，让多种数据分类更加明确。</p><section><h3>语法<a href="#语法"><span>#</span></a></h3><p>以加法操作为例，直接写在<code>store.js</code>中</p><p><code>store.js</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Vuex</span><span> </span><span>from</span><span> </span><span>'vuex'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>Vuex</span><span>)</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>addCountOptions</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>namespaced</span><span>:</span><span> </span><span>true</span><span>, </span><span>//开启命名空间</span></div></div><div><div><div>8</div></div><div><span>    </span><span>actions</span><span>:</span><span> {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>jiaWait</span><span><span>(</span><span>context</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>            </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>11</div></div><div><span>                </span><span>if</span><span><span> (</span><span>context</span><span>.</span></span><span>state</span><span>.</span><span>sum</span><span> </span><span>%</span><span> </span><span>2</span><span> </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>12</div></div><div><span><span>                    </span></span><span>context</span><span>.</span><span>commit</span><span>(</span><span>'JIA'</span><span><span>, </span><span>value</span><span>)</span></span></div></div><div><div><div>13</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>}, </span><span>500</span><span>)</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span>    </span><span>mutations</span><span>:</span><span> {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>JIA</span><span><span>(</span><span>state</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>context</span><span>.</span><span>sum</span><span> </span><span>+=</span><span><span> </span><span>value</span></span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span>        </span><span>JIAN</span><span><span>(</span><span>state</span><span>, </span><span>value</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>context</span><span>.</span><span>sum</span><span> </span><span>-=</span><span><span> </span><span>value</span></span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>25</div></div><div><span>    </span><span>state</span><span>:</span><span> {</span></div></div><div><div><div>26</div></div><div><span>        </span><span>sum</span><span>:</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>27</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'tom'</span><span>,</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>29</div></div><div><span>    </span><span>getters</span><span>:</span><span> {</span></div></div><div><div><div>30</div></div><div><span>        </span><span>bigSum</span><span><span>(</span><span>state</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span>            </span><span>return</span><span><span> </span><span>state</span><span>.</span></span><span>sum</span><span> </span><span>*</span><span> </span><span>10</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>new</span><span><span> </span><span>Vuex</span><span>.</span></span><span>Store</span><span>({</span></div></div><div><div><div>37</div></div><div><span>    </span><span>modules</span><span>:</span><span> {</span></div></div><div><div><div>38</div></div><div><span>        </span><span>addCountOptions</span><span>,</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>40</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>或者写入单独的<code>js</code>文件中</p><p><code>addCount.js</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>namespaced</span><span>:</span><span> </span><span>true</span><span>, </span><span>//开启命名空间</span></div></div><div><div><div>3</div></div><div><span>    </span><span>actions</span><span>:</span><span> {},</span></div></div><div><div><div>4</div></div><div><span>    </span><span>mutations</span><span>:</span><span> {},</span></div></div><div><div><div>5</div></div><div><span>    </span><span>state</span><span>:</span><span> {},</span></div></div><div><div><div>6</div></div><div><span>    </span><span>getters</span><span>:</span><span> {},</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>store.js</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>Vuex</span><span> </span><span>from</span><span> </span><span>'vuex'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>addCountOptions</span><span> </span><span>from</span><span> </span><span>'./addCount'</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>Vuex</span><span>)</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>new</span><span><span> </span><span>Vuex</span><span>.</span></span><span>Store</span><span>({</span></div></div><div><div><div>8</div></div><div><span>    </span><span>modules</span><span>:</span><span> {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>addCountOptions</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>11</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>读取<code>state</code>数据<a href="#读取state数据"><span>#</span></a></h3><p>开启命名空间后，组件中读取<code>state</code>数据：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//方式一：自己直接读取</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>state</span><span>.</span><span>addCountOptions</span><span>.</span><span>sum</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>//方式二：借助mapState读取：</span></div></div><div><div><div>5</div></div><div><span>// 用 mapState 取 addCountOptions 中的state 必须加上 'addCountOptions'</span></div></div><div><div><div>6</div></div><div><span>...</span><span>mapState</span><span>(</span><span>'countAbout'</span><span>,[</span><span>'sum'</span><span>,</span><span>'name'</span><span>]),</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>读取<code>getters</code>数据<a href="#读取getters数据"><span>#</span></a></h3><p>开启命名空间后，组件中读取<code>getters</code>数据：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//方式一：自己直接读取，getters中没有addCountOptions属性，</span></div></div><div><div><div>2</div></div><div><span>    </span><span>//因此只能使用 addCountOptions/bigSum</span></div></div><div><div><div>3</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>getters</span><span>[</span><span>'addCountOptions/bigSum'</span><span>]</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>//方式二：借助mapGetters读取：</span></div></div><div><div><div>6</div></div><div><span>...</span><span>mapGetters</span><span>(</span><span>'addCountOptions'</span><span>,[</span><span>'bigSum'</span><span>])</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>调用<code>dispatch</code><a href="#调用dispatch"><span>#</span></a></h3><p>开启命名空间后，组件中调用<code>dispatch</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//方式一：自己直接dispatch</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>dispatch</span><span>(</span><span>'addCountOptions/jiaWait'</span><span><span>,</span><span>value</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>//方式二：借助mapActions：在标签绑定method时必须手动传入参数 incrementWait(value)</span></div></div><div><div><div>5</div></div><div><span>...</span><span>mapActions</span><span>(</span><span>'addCountOptions'</span><span>,{</span><span>incrementWait</span><span>:</span><span>'jiaWait'</span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>调用<code>commit</code><a href="#调用commit"><span>#</span></a></h3><p>开启命名空间后，组件中调用<code>commit</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//方式一：自己直接commit</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>commit</span><span>(</span><span>'addCountOptions/JIA'</span><span><span>,</span><span>value</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>this</span><span>.</span><span>$store</span><span>.</span><span>commit</span><span>(</span><span>'addCountOptions/JIAN'</span><span><span>,</span><span>value</span><span>)</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>//方式二：借助mapMutations：在标签绑定method时必须手动传入参数 increment(value)</span></div></div><div><div><div>6</div></div><div><span>...</span><span>mapMutations</span><span>(</span><span>'addCountOptions'</span><span>,{</span><span>increment</span><span>:</span><span>'JIA'</span><span>,</span><span>decrement</span><span>:</span><span>'JIAN'</span><span>}),</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-cha-jian/</id>
      <title type="text">Vue 插件</title>
      <published>2022-10-08T02:15:18.000Z</published>
      <updated>2022-10-08T02:15:18.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-cha-jian/"/>
      <summary type="text">插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制,一般有下面几种： 添加全局方法或者 property 。如： vue custom element 添加全局资源：指令/过滤器/过渡等。如 vue touch 通过全局混入来添加一些组件选项。如 vue router 添加 Vue…</summary>
      <content type="html"><![CDATA[<p>插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制,一般有下面几种：</p>
<ul>
<li>
<p>添加全局方法或者 <code>property</code>。如：<code>vue-custom-element</code></p>
</li>
<li>
<p>添加全局资源：指令/过滤器/过渡等。如 <code>vue-touch</code></p>
</li>
<li>
<p>通过全局混入来添加一些组件选项。如 <code>vue-router</code></p>
</li>
<li>
<p>添加 Vue 实例方法，通过把它们添加到 <code>Vue.prototype</code> 上实现</p>
</li>
<li>
<p>一个库，提供自己的 API，同时提供上面提到的一个或多个功能。如 <code>vuex</code>，<code>vue-router</code></p>
</li>
</ul>
<section><h2>定义插件<a href="#定义插件"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>对象</span><span>.</span></span><span>install</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>Vue</span><span>, </span><span>options</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>// 1. 添加全局过滤器</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Vue</span><span>.</span><span>filter</span><span>(</span><span>...</span><span>.)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>// 2. 添加全局指令</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Vue</span><span>.</span><span>directive</span><span>(</span><span>...</span><span>.)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 3. 配置全局混入(合)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Vue</span><span>.</span><span>mixin</span><span>(</span><span>...</span><span>.)</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 4. 添加实例方法</span></div></div><div><div><div>12</div></div><div><span>    </span><span>Vue</span><span>.</span><span>prototype</span><span>.</span><span>$myMethod</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span><span>...</span><span>}</span></div></div><div><div><div>13</div></div><div><span>    </span><span>Vue</span><span>.</span><span>prototype</span><span>.</span><span>$myProperty</span><span> </span><span>=</span><span><span> </span><span>xxxx</span></span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>使用插件<a href="#使用插件"><span>#</span></a></h2><p>通过全局方法 <code>Vue.use()</code> 使用插件。它需要在你调用 <code>new Vue()</code> 启动应用之前完成：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 调用 `MyPlugin.install(Vue)`</span></div></div><div><div><div>2</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>MyPlugin</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// ...组件选项</span></div></div><div><div><div>6</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>本质：包含<code>install</code>方法的一个对象，<code>install</code>的第一个参数是<code>Vue</code>，第二个以后的参数是插件使用者传递的数据。</p></section>
<section><h2>具体案例<a href="#具体案例"><span>#</span></a></h2><p><code>plugin.js</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>install</span><span><span>(</span><span>Vue</span><span>, </span><span>x</span><span>, </span><span>y</span><span>, </span><span>z</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>z</span><span>)</span></div></div><div><div><div>4</div></div><div><span>        </span><span>//全局过滤器</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>Vue</span><span>.</span><span>filter</span><span>(</span><span>'mySlice'</span><span>, </span><span>function</span><span><span> (</span><span>value</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>            </span><span>return</span><span><span> </span><span>value</span><span>.</span></span><span>slice</span><span>(</span><span>0</span><span>, </span><span>4</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>        </span><span>//定义全局指令</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>Vue</span><span>.</span><span>directive</span><span>(</span><span>'fbind'</span><span>, {</span></div></div><div><div><div>11</div></div><div><span>            </span><span>//指令与元素成功绑定时（一上来）</span></div></div><div><div><div>12</div></div><div><span>            </span><span>bind</span><span><span>(</span><span>element</span><span>, </span><span>binding</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span><span>                </span></span><span>element</span><span>.</span><span>value</span><span> </span><span>=</span><span><span> </span><span>binding</span><span>.</span></span><span>value</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span>            </span><span>//指令所在元素被插入页面时</span></div></div><div><div><div>16</div></div><div><span>            </span><span>inserted</span><span><span>(</span><span>element</span><span>, </span><span>binding</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span><span>                </span></span><span>element</span><span>.</span><span>focus</span><span>()</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>19</div></div><div><span>            </span><span>//指令所在的模板被重新解析时</span></div></div><div><div><div>20</div></div><div><span>            </span><span>update</span><span><span>(</span><span>element</span><span>, </span><span>binding</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span><span>                </span></span><span>element</span><span>.</span><span>value</span><span> </span><span>=</span><span><span> </span><span>binding</span><span>.</span></span><span>value</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>        </span><span>//定义混入</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>Vue</span><span>.</span><span>mixin</span><span>({</span></div></div><div><div><div>27</div></div><div><span>            </span><span>data</span><span>() {</span></div></div><div><div><div>28</div></div><div><span>                </span><span>return</span><span> {</span></div></div><div><div><div>29</div></div><div><span>                    </span><span>x</span><span>:</span><span> </span><span>100</span><span>,</span></div></div><div><div><div>30</div></div><div><span>                    </span><span>y</span><span>:</span><span> </span><span>200</span></div></div><div><div><div>31</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>        </span><span>//给Vue原型上添加一个方法（vm和vc就都能用了）</span></div></div><div><div><div>36</div></div><div><span>        </span><span>Vue</span><span>.</span><span>prototype</span><span>.</span><span>hello</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> { </span><span>alert</span><span>(</span><span>'你好啊aaaa'</span><span>) }</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>main.js</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 引入插件</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>plugin</span><span> </span><span>from</span><span> </span><span>'./plugin'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 使用插件</span></div></div><div><div><div>5</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>plugin</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后就可以在别的组件使用插件里的功能了。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/mixin-hun-ru/</id>
      <title type="text">Mixin (混入)</title>
      <published>2022-10-08T01:14:08.000Z</published>
      <updated>2022-10-08T01:14:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/mixin-hun-ru/"/>
      <summary type="text">混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时，所有混入对象的选项将被混入该组件本身的选项 Mixins 是一种分发 Vue 组件中可复用功能的非常灵活的一种方式。页面的风格不用，但是执行的方法和需要的数据类似就可以选…</summary>
      <content type="html"><![CDATA[<section><h1>mixin<a href="#mixin"><span>#</span></a></h1><p><strong>混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时，所有混入对象的选项将被混入该组件本身的选项</strong></p><p>Mixins 是一种分发 Vue 组件中可复用功能的非常灵活的一种方式。页面的风格不用，但是执行的方法和需要的数据类似就可以选择使用混入</p><p>混合对于封装一小段想要复用的代码来讲是有用的。它们当然不是唯一可行的。混合很好，它不需要传递状态，但是这种模式当然也可能会被滥用</p><section><h2>区别<a href="#区别"><span>#</span></a></h2><section><h3>mixin混入对象和Vuex的区别<a href="#mixin混入对象和vuex的区别"><span>#</span></a></h3><ul>
<li>
<p>Vuex是<strong>状态共享管理</strong>，所以Vuex中的<strong>所有变量和方法都是可以读取和更改并相互影响的</strong></p>
</li>
<li>
<p>mixin可以<strong>定义公用的变量或方法</strong>，但是mixin中的<strong>数据是不共享的</strong>，也就是每个组件中的mixin实例都是不一样的，都是<strong>单独存在的个体，不存在相互影响的</strong></p>
</li>
<li>
<p>mixin混入对象值为函数的同名函数选项将会进行递归合并为数组，两个函数都会执行，只不过先执行mixin中的同名函数</p>
</li>
<li>
<p>mixin混入对象值为对象的同名对象将会进行替换，都优先执行组件内的同名对象，也就是组件内的同名对象将mixin混入对象的同名对象进行覆盖</p>
</li>
</ul></section><section><h3>与公共组件的区别<a href="#与公共组件的区别"><span>#</span></a></h3><ul>
<li>组件：在父组件中引入组件，相当于在父组件中给出一片独立的空间供子组件使用，然后根据props来传值，但本质上两者是相对独立的</li>
<li>Mixins：则是在引入组件之后与组件中的对象和方法进行合并，相当于扩展了父组件的对象与方法，可以理解为形成了一个新的组件</li>
</ul></section></section><section><h2>基本使用<a href="#基本使用"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 定义一个混入对象 mixin.js</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>myMixin</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>hello</span><span>() {</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'hello from mixin!'</span><span>)</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span>    </span><span>mounted</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>this</span><span>.</span><span>hello</span><span>()</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 定义一个使用混入对象的组件</span></div></div><div><div><div>14</div></div><div><span>import</span><span> { </span><span>myMixin</span><span> } </span><span>from</span><span> </span><span>'../mixin'</span></div></div><div><div><div>15</div></div><div><span>const</span><span> </span><span>MixinComponent</span><span> </span><span>=</span><span><span> </span><span>Vue</span><span>.</span></span><span>extend</span><span>({</span></div></div><div><div><div>16</div></div><div><span>    </span><span>mixins</span><span>:</span><span><span> [</span><span>myMixin</span><span>],</span></span></div></div><div><div><div>17</div></div><div><span>})</span></div></div><div><div><div>18</div></div><div><span>// or</span></div></div><div><div><div>19</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'MixinComponent'</span><span>,</span></div></div><div><div><div>21</div></div><div><span>    </span><span>mixins</span><span>:</span><span> [</span><span>myMixin</span><span>],</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>选项合并规则<a href="#选项合并规则"><span>#</span></a></h2><p>当组件和混入对象含有同名选项时，这些选项将以恰当的方式混合。</p><section><h3>数据对象内<a href="#数据对象内"><span>#</span></a></h3><p>mixin 的数据对象和组件的数据发生冲突时以组件数据优先。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>mixin</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span> {</span></div></div><div><div><div>4</div></div><div><span>            </span><span>message</span><span>:</span><span> </span><span>'hello'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>            </span><span>foo</span><span>:</span><span> </span><span>'abc'</span><span>,</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>10</div></div><div><span>    </span><span>mixins</span><span>:</span><span><span> [</span><span>mixin</span><span>],</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span> {</span></div></div><div><div><div>13</div></div><div><span>            </span><span>message</span><span>:</span><span> </span><span>'goodbye'</span><span>,</span></div></div><div><div><div>14</div></div><div><span>            </span><span>bar</span><span>:</span><span> </span><span>'def'</span><span>,</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span>    </span><span>created</span><span>() {</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>data</span><span>)</span></div></div><div><div><div>19</div></div><div><span>        </span><span>// =&gt; { message: "goodbye", foo: "abc", bar: "def" }</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>钩子函数<a href="#钩子函数"><span>#</span></a></h3><p>同名钩子函数将会混合为一个数组，都将被调用到，但是混入对象的钩子将在组件自身钩子之前调用。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>mixin</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>created</span><span>() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'混入对象的钩子被调用'</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>7</div></div><div><span>    </span><span>mixins</span><span>:</span><span><span> [</span><span>mixin</span><span>],</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>created</span><span>() {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'组件钩子被调用'</span><span>)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>11</div></div><div><span>})</span></div></div><div><div><div>12</div></div><div><span>// =&gt; "混入对象的钩子被调用"</span></div></div><div><div><div>13</div></div><div><span>// =&gt; "组件钩子被调用"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>值为对象的选项<a href="#值为对象的选项"><span>#</span></a></h3><p>值为对象的选项，例如 <code>methods</code>，<code>components</code> 和 <code>directives</code>，将被混合为同一个对象。两个对象键名冲突时，取组件对象的键值对。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>mixin</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>foo</span><span>() {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'foo'</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span>        </span><span>conflicting</span><span>() {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'from mixin'</span><span>)</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>var</span><span><span> </span><span>vm</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>12</div></div><div><span>    </span><span>mixins</span><span>:</span><span><span> [</span><span>mixin</span><span>],</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>bar</span><span>() {</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'bar'</span><span>)</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span>        </span><span>conflicting</span><span>() {</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'from self'</span><span>)</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span>})</span></div></div><div><div><div>22</div></div><div><span><span>vm</span><span>.</span></span><span>foo</span><span>() </span><span>// =&gt; "foo"</span></div></div><div><div><div>23</div></div><div><span><span>vm</span><span>.</span></span><span>bar</span><span>() </span><span>// =&gt; "bar"</span></div></div><div><div><div>24</div></div><div><span><span>vm</span><span>.</span></span><span>conflicting</span><span>() </span><span>// =&gt; "from self"</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>全局混入（不推荐）<a href="#全局混入不推荐"><span>#</span></a></h2><p>全局混合被注册到了每个单一组件上。因此，它们的使用场景极其有限并且要非常的小心。一个我能想到的用途就是它像一个插件，你需要赋予它访问所有东西的权限。但即使在这种情况下，我也对你正在做的保持警惕，尤其是你在应用中扩展的函数，可能对你来说是不可知的。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>Vue</span><span>.</span></span><span>mixin</span><span>({</span></div></div><div><div><div>2</div></div><div><span>    </span><span>mounted</span><span>() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"我是mixin"</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>})</span></div></div><div><div><div>6</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>7</div></div><div><span>    </span><span>//...</span></div></div><div><div><div>8</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>注意:</strong> 一旦使用全局混入对象，将会影响到<strong>所有</strong>之后创建的 Vue 实例。使用恰当时，可以为自定义对象注入处理逻辑</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 为自定义的选项 'myOption' 注入一个处理器。</span></div></div><div><div><div>2</div></div><div><span><span>Vue</span><span>.</span></span><span>mixin</span><span>({</span></div></div><div><div><div>3</div></div><div><span>    </span><span>created</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>const</span><span> </span><span>myOption</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>$options</span><span>.</span><span>myOption</span></div></div><div><div><div>5</div></div><div><span>        </span><span>if</span><span><span> (</span><span>myOption</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>myOption</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span>})</span></div></div><div><div><div>10</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>11</div></div><div><span>    </span><span>myOption</span><span>:</span><span> </span><span>'hello!'</span><span>,</span></div></div><div><div><div>12</div></div><div><span>})</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// =&gt; "hello!"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-router/</id>
      <title type="text">vue-router</title>
      <published>2022-10-08T01:14:08.000Z</published>
      <updated>2022-10-08T01:14:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-router/"/>
      <summary type="text">vue router 是Vue.js官方的路由插件，它和vue.js是深度集成的，适合用于构建单页面应用。 我们可以访问其官方网站对其进行学习：https://router.vuejs.org/zh/ vue router 是基于路由和组件的 路由用户设定访问路径的，将路径和组件映射起来。 在 vu…</summary>
      <content type="html"><![CDATA[<p><code>vue-router</code>是Vue.js官方的路由插件，它和vue.js是深度集成的，适合用于构建单页面应用。
我们可以访问其官方网站对其进行学习：<a href="https://router.vuejs.org/zh/" target="_blank">https://router.vuejs.org/zh/</a></p>
<p><code>vue-router</code>是基于路由和组件的</p>
<ul>
<li>路由用户设定访问路径的，将路径和组件映射起来。</li>
<li>在<code>vue-router</code>的单页面应用中，页面的路径的改变就是组件的切换</li>
</ul>
<section><h2>路由的定义<a href="#路由的定义"><span>#</span></a></h2><ul>
<li>
<p>后端路由：对于普通的网站，所有的超链接都是 URL 地址，所有的 url 地址都对应服务器上对应的资源</p>
<p><strong>后端路由是处理请求的回调函数</strong></p>
</li>
<li>
<p>前端路由：对于单页面应用程序来说，主要通过 URL 中的 hash(#号)来实现不同页面之间的切换，同时，hash 有一个特点：HTTP 请求中不会包含 hash 相关的内容；所以，单页面程序中的页面跳转主要用 hash 实现,在单页面应用中这就叫做前端路由</p>
<p><strong>前端路由让构建单页面应用变得易如反掌</strong></p>
</li>
</ul></section>
<section><h2>基本使用<a href="#基本使用"><span>#</span></a></h2><ol>
<li>
<p>安装vue-router，命令：<code>npm i vue-router</code></p>
</li>
<li>
<p>编写router配置项:</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//引入VueRouter</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>VueRouter</span><span> </span><span>from</span><span> </span><span>'vue-router'</span></div></div><div><div><div>3</div></div><div><span>//引入Luyou 组件</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>About</span><span> </span><span>from</span><span> </span><span>'../components/About'</span></div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>Home</span><span> </span><span>from</span><span> </span><span>'../components/Home'</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>//创建router实例对象，去管理一组一组的路由规则</span></div></div><div><div><div>8</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>VueRouter</span><span>({</span></div></div><div><div><div>9</div></div><div><span>    </span><span>/*</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>route这个配置对象中的route表示路由器匹配规则的意思</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>每个路由规则都是一个规则对象,身上有两个必须的属性:</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>第一个属性为path,表示监听哪个路由链接地址</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>第二个属性为component,表示如果路由前面匹配到了path路径,则展示component属性对于的组件</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>*/</span></div></div><div><div><div>15</div></div><div><span>  </span><span>routes</span><span>:</span><span>[</span></div></div><div><div><div>16</div></div><div><span>         </span><span>//通过redirect属性设置路由重定向,当访问根目录的时候自动跳转到/login目录</span></div></div><div><div><div>17</div></div><div><span><span>         </span></span><span>{</span></div></div><div><div><div>18</div></div><div><span>             </span><span>path</span><span>:</span><span> </span><span>"/"</span><span>,</span></div></div><div><div><div>19</div></div><div><span>             </span><span>redirect</span><span>:</span><span> </span><span>"/login"</span></div></div><div><div><div>20</div></div><div><span><span>         </span></span><span>},</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>23</div></div><div><span>      </span><span>path</span><span>:</span><span>'/about'</span><span>,</span></div></div><div><div><div>24</div></div><div><span>      </span><span>component</span><span>:</span><span>About</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>27</div></div><div><span>      </span><span>path</span><span>:</span><span>'/home'</span><span>,</span></div></div><div><div><div>28</div></div><div><span>      </span><span>component</span><span>:</span><span>Home</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>31</div></div><div><span>})</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>//暴露router</span></div></div><div><div><div>34</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>router</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
<li>
<p>挂载到vue实例</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>App</span><span> </span><span>from</span><span> </span><span>'./App.vue'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>VueRouter</span><span> </span><span>from</span><span> </span><span>'vue-router'</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>router</span><span> </span><span>from</span><span> </span><span>'./router'</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>Vue</span><span>.</span></span><span>use</span><span><span>(</span><span>VueRouter</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>9</div></div><div><span>    </span><span>render</span><span>:</span><span><span> (</span><span>h</span><span>) </span></span><span>=&gt;</span><span> </span><span>h</span><span><span>(</span><span>App</span><span>),</span></span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>router</span><span>,</span></div></div><div><div><div>11</div></div><div><span>}).</span><span>$mount</span><span>(</span><span>'#app'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>实现切换（active-class可配置高亮样式）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>active-class</span><span>=</span><span>"active"</span><span> </span><span>to</span><span>=</span><span>"/about"</span><span>&gt;About&lt;/</span><span>router-link</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>指定展示位置</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>router-view</span><span>&gt;&lt;/</span><span>router-view</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section>
<section><h2>多级（嵌套）路由<a href="#多级嵌套路由"><span>#</span></a></h2><p>如果想要在一层路由组件下面开启第二层组件，不能够直接通过二级的路由链接得到二级的路由组件，因为这样一级的链接组件就会消失</p><p>需要通过<code>routes</code>路由器匹配规则中相应规则的 <code>children</code> 属性，在该属性构成的数组中写入子路由来实现路由嵌套的功能</p><ol>
<li>
<p>配置路由规则，使用<code>children</code>配置项：</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span>  </span><span>new</span><span> </span><span>VueRouter</span><span>({</span></div></div><div><div><div>2</div></div><div><span>    </span><span>routes</span><span>:</span><span>[</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span>            </span><span>path</span><span>:</span><span>'/about'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>            </span><span>component</span><span>:</span><span><span>About</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>8</div></div><div><span>            </span><span>path</span><span>:</span><span>'/home'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>            </span><span>component</span><span>:</span><span><span>Home</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>            </span><span>children</span><span>:</span><span>[ </span><span>//通过children配置子级路由</span></div></div><div><div><div>11</div></div><div><span><span>                </span></span><span>{</span></div></div><div><div><div>12</div></div><div><span>                    </span><span>path</span><span>:</span><span>'news'</span><span>, </span><span>//此处一定不要写：/news</span></div></div><div><div><div>13</div></div><div><span>                    </span><span>component</span><span>:</span><span>News</span></div></div><div><div><div>14</div></div><div><span><span>                </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span><span>                </span></span><span>{</span></div></div><div><div><div>16</div></div><div><span>                    </span><span>path</span><span>:</span><span>'message'</span><span>,</span><span>//此处一定不要写：/message</span></div></div><div><div><div>17</div></div><div><span>                    </span><span>component</span><span>:</span><span>Message</span></div></div><div><div><div>18</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>]</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>22</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
<li>
<p>跳转（要写完整路径）：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>to</span><span>=</span><span>"/home/news"</span><span>&gt;News&lt;/</span><span>router-link</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>指定展示位置</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>router-view</span><span>&gt;&lt;/</span><span>router-view</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section>
<section><h2>命名路由<a href="#命名路由"><span>#</span></a></h2><p>通过在<code>&lt;router-link&gt;&lt;/router-link&gt;</code>的组件中 <code>to</code> 指向一个带有 <code>name</code> 属性的对象，而在路由规则中也添加一个对应的 <code>name</code> 属性实现路由跳转，该方法能让我们更加轻松的进行路由规则的匹配</p><ol>
<li>给路由命名：</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span>  </span><span>new</span><span> </span><span>VueRouter</span><span>({</span></div></div><div><div><div>2</div></div><div><span>    </span><span>routes</span><span>:</span><span>[</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span>            </span><span>path</span><span>:</span><span>'/demo'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>            </span><span>component</span><span>:</span><span><span>Demo</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>            </span><span>children</span><span>:</span><span>[</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>{</span></div></div><div><div><div>8</div></div><div><span>                    </span><span>path</span><span>:</span><span>'test'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>                    </span><span>component</span><span>:</span><span><span>Test</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>                    </span><span>children</span><span>:</span><span>[</span></div></div><div><div><div>11</div></div><div><span><span>                        </span></span><span>{</span></div></div><div><div><div>12</div></div><div><span>                              </span><span>name</span><span>:</span><span>'hello'</span><span> </span><span>//给路由命名</span></div></div><div><div><div>13</div></div><div><span><span>                            </span></span><span>path</span><span>:</span><span>'welcome'</span><span>,</span></div></div><div><div><div>14</div></div><div><span>                            </span><span>component</span><span>:</span><span><span>Hello</span><span>,</span></span></div></div><div><div><div>15</div></div><div><span><span>                        </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>                    </span></span><span>]</span></div></div><div><div><div>17</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>]</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>21</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>简化跳转：</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!--简化前，需要写完整的路径 --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>to</span><span>=</span><span>"/demo/test/welcome"</span><span>&gt;跳转&lt;/</span><span>router-link</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!--简化后，直接通过名字跳转 --&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>:to</span><span>=</span><span>"{name:'hello'}"</span><span>&gt;跳转&lt;/</span><span>router-link</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;!--简化写法配合传递参数 --&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;</span><span>router-link</span></div></div><div><div><div>9</div></div><div><span>  </span><span>:to</span><span>=</span><span>"{</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>name:'hello',</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>query:{</span></div></div><div><div><div>12</div></div><div><span><span>       </span></span><span>id:666,</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>title:'你好'</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>}"</span></div></div><div><div><div>16</div></div><div><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>跳转</span></div></div><div><div><div>18</div></div><div><span>&lt;/</span><span>router-link</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>路由传参<a href="#路由传参"><span>#</span></a></h2><p>一个 Vue 组件内置的<code>$route</code> 属性可以代表着正在进行跳转的路由</p><section><h3>query<a href="#query"><span>#</span></a></h3><p>当在路由器中链接中使用查询字符串(<code>url</code> 后面的<code>?</code>后的一系列值),不用修改路由规则中的 <code>path</code> 属性的路径，在路由视图的组件中可用通过 <code>this.$route</code> 获取该路由实例，通过该实例的 <code>query</code> 属性获取传入的参数的属性和值</p><ol>
<li>传递参数</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 跳转并携带query参数，to的字符串写法 --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>to</span><span>=</span><span>"/home/message/detail?id=666&amp;title=你好"</span><span>&gt;跳转&lt;/</span><span>router-link</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!-- 跳转并携带query参数，to的对象写法 --&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;</span><span>router-link</span></div></div><div><div><div>6</div></div><div><span>    </span><span>:to</span><span>=</span><span>"{</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>path:'/home/message/detail',</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>query:{</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>id:666,</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>title:'你好'</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}"</span></div></div><div><div><div>13</div></div><div><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>跳转</span></div></div><div><div><div>15</div></div><div><span>&lt;/</span><span>router-link</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>接收参数：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>$route</span><span>.</span></span><span>query</span><span>.</span><span>id</span></div></div><div><div><div>2</div></div><div><span><span>$route</span><span>.</span></span><span>query</span><span>.</span><span>title</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>params<a href="#params"><span>#</span></a></h3><p>使用 <code>params</code> 来获取参数需要在路由规则中定义参数，然后在路由器链接中需要在后面跟上对应数量的<code>/+值</code>传给参数实现传参，通过该实例的 <code>params</code> 属性获取传入的参数的属性和值</p><ol>
<li>配置路由，声明接收<code>params</code>参数</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span>  </span><span>new</span><span> </span><span>VueRouter</span><span>({</span></div></div><div><div><div>2</div></div><div><span>    </span><span>routes</span><span>:</span><span>[</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span>            </span><span>path</span><span>:</span><span>'/home'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>            </span><span>component</span><span>:</span><span><span>Home</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>            </span><span>children</span><span>:</span><span>[</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>{</span></div></div><div><div><div>8</div></div><div><span>                    </span><span>path</span><span>:</span><span>'news'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>                    </span><span>component</span><span>:</span><span><span>News</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>},</span></div></div><div><div><div>11</div></div><div><span><span>                </span></span><span>{</span></div></div><div><div><div>12</div></div><div><span>                    </span><span>component</span><span>:</span><span><span>Message</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>                    </span><span>children</span><span>:</span><span>[</span></div></div><div><div><div>14</div></div><div><span><span>                        </span></span><span>{</span></div></div><div><div><div>15</div></div><div><span>                            </span><span>name</span><span>:</span><span>'xiangqing'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>                            </span><span>path</span><span>:</span><span>'detail/:id/:title'</span><span>, </span><span>//使用占位符声明接收params参数</span></div></div><div><div><div>17</div></div><div><span>                            </span><span>component</span><span>:</span><span><span>Detail</span><span>,</span></span></div></div><div><div><div>18</div></div><div><span><span>                        </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>                    </span></span><span>]</span></div></div><div><div><div>20</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>            </span></span><span>]</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>24</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>传递参数</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 跳转并携带params参数，to的字符串写法 --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>:to</span><span>=</span><span>"/home/message/detail/666/你好"</span><span>&gt;跳转&lt;/</span><span>router-link</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!-- 跳转并携带params参数，to的对象写法 --&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;</span><span>router-link</span></div></div><div><div><div>6</div></div><div><span>  </span><span>:to</span><span>=</span><span>"{</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>name:'xiangqing', // 必须使用name属性，不能用path</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>params:{</span></div></div><div><div><div>9</div></div><div><span><span>       </span></span><span>id:666,</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>title:'你好'</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}"</span></div></div><div><div><div>13</div></div><div><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>跳转</span></div></div><div><div><div>15</div></div><div><span>&lt;/</span><span>router-link</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>特别注意</strong>：路由携带<code>params</code>参数时，若使用<code>to</code>的对象写法，则不能使用<code>path</code>配置项，必须使用<code>name</code>配置！</p><ol>
<li>接收参数：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>$route</span><span>.</span></span><span>params</span><span>.</span><span>id</span></div></div><div><div><div>2</div></div><div><span><span>$route</span><span>.</span></span><span>params</span><span>.</span><span>title</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>路由的props<a href="#路由的props"><span>#</span></a></h2><p>作用：让路由组件更方便的收到参数</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span>  </span><span>new</span><span> </span><span>VueRouter</span><span>({</span></div></div><div><div><div>2</div></div><div><span>    </span><span>routes</span><span>:</span><span> [</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span>            </span><span>name</span><span>:</span><span>'xiangqing'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>            </span><span>path</span><span>:</span><span>'detail'</span><span>,</span></div></div><div><div><div>6</div></div><div><span>            </span><span>component</span><span>:</span><span><span>Detail</span><span>,</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>            </span><span>//第一种写法：props值为对象，该对象中所有的key-value的组合最终都会通过props传给Detail组件</span></div></div><div><div><div>9</div></div><div><span>            </span><span>// props:{a:900}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>            </span><span>//第二种写法：props值为布尔值，布尔值为true，</span></div></div><div><div><div>12</div></div><div><span>            </span><span>//  则把路由收到的所有params参数通过props传给Detail组件</span></div></div><div><div><div>13</div></div><div><span>            </span><span>// props:true</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>            </span><span>//第三种写法：props值为函数，该函数返回的对象中每一组key-value都会通过props传给Detail组件</span></div></div><div><div><div>16</div></div><div><span>            </span><span>props</span><span><span>(</span><span>$route</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>                </span><span>return</span><span> {</span></div></div><div><div><div>18</div></div><div><span>                  </span><span>id</span><span>:</span><span><span> </span><span>$route</span><span>.</span></span><span>query</span><span>.</span><span>id</span><span>,</span></div></div><div><div><div>19</div></div><div><span>                  </span><span>title</span><span>:</span><span><span>$route</span><span>.</span></span><span>query</span><span>.</span><span>title</span><span>,</span></div></div><div><div><div>20</div></div><div><span>                  </span><span>a</span><span>:</span><span> </span><span>1</span><span>,</span></div></div><div><div><div>21</div></div><div><span>                  </span><span>b</span><span>:</span><span> </span><span>'hello'</span></div></div><div><div><div>22</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>26</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>对应组件的具体代码</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>&lt;</span><span>h1</span><span>&gt;Detail&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>&lt;</span><span>li</span><span>&gt;消息编号：{{id}}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>&lt;</span><span>li</span><span>&gt;消息标题：{{title}}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>&lt;</span><span>li</span><span>&gt;a:{{a}}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;</span><span>li</span><span>&gt;b:{{b}}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Detail'</span><span>,</span></div></div><div><div><div>14</div></div><div><span>    </span><span>props</span><span>:</span><span> [</span><span>'id'</span><span>, </span><span>'title'</span><span>, </span><span>'a'</span><span>, </span><span>'b'</span><span>],</span></div></div><div><div><div>15</div></div><div><span>    </span><span>mounted</span><span> () {</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>$route</span><span>);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2><span><span>route与route 与 </span><span><span><span></span><span>r</span><span>o</span><span>u</span><span>t</span><span>e</span><span>与</span></span></span></span>router<a href="#route与route-与-route与router"><span>#</span></a></h2><ul>
<li><code>this.$route</code> 是路由的参数对象，所有的路由参数如 <code>pararms</code> 和 <code>query</code> 都属于该属性的参数</li>
<li><code>this.$router</code> 是一个路由导航对象，用它可以方便的使用 JS 代码，实现路由器的前进，后退，跳转到新的 <code>url</code> 地址</li>
</ul><section><h3>$route 表示(当前路由信息对象)<a href="#route-表示当前路由信息对象"><span>#</span></a></h3><p>表示当前激活的路由的状态信息，包含了当前 URL 解析得到的信息，还有 URL 匹配到的 route records（路由记录）。
路由信息对象：即<code>$router</code>会被注入每个组件中，可以利用它进行一些信息的获取。</p><ol>
<li>
<p><code>$route.path</code></p>
<p>字符串，对应当前路由的路径，总是解析为绝对路径，如 “/foo/bar”。</p>
</li>
<li>
<p><code>$route.params</code></p>
<p>一个 key/value 对象，包含了 动态片段 和 全匹配片段，
如果没有路由参数，就是一个空对象。</p>
</li>
<li>
<p><code>$route.query</code></p>
<p>一个 key/value 对象，表示 URL 查询参数。
例如，对于路径 <code>/foo?user=1</code>，则有 <code>$route.query.user == 1</code>，
如果没有查询参数，则是个空对象。</p>
</li>
<li>
<p><code>$route.hash</code></p>
<p>当前路由的 hash 值 (不带 #) ，如果没有 hash 值，则为空字符串。锚点</p>
</li>
<li>
<p><code>$route.fullPath</code></p>
<p>完成解析后的 URL，包含查询参数和 hash 的完整路径。</p>
</li>
<li>
<p><code>$route.matched</code></p>
<p>数组，包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。</p>
</li>
<li>
<p><code>$route.name</code></p>
<p>当前路径名字</p>
</li>
<li>
<p><code>$route.meta</code></p>
<p>对象，路由元信息，可以以对象的形式添加自定义的信息</p>
</li>
</ol></section><section><h3>$router对象<a href="#router对象"><span>#</span></a></h3><p>全局的路由实例，是<code>router</code>构造方法的实例。
在 Vue 实例内部，你可以通过 <code>$router</code> 访问路由实例</p><section><h4>this.$router.push()<a href="#thisrouterpush"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 字符串</span></div></div><div><div><div>2</div></div><div><span>      </span><span>this</span><span>.</span><span>$router</span><span>.</span><span>push</span><span>(</span><span>'home'</span><span>)</span></div></div><div><div><div>3</div></div><div><span>// 对象</span></div></div><div><div><div>4</div></div><div><span>      </span><span>this</span><span>.</span><span>$router</span><span>.</span><span>push</span><span>({ </span><span>path</span><span>:</span><span> </span><span>'home'</span><span> })</span></div></div><div><div><div>5</div></div><div><span>// 命名的路由</span></div></div><div><div><div>6</div></div><div><span>      </span><span>this</span><span>.</span><span>$router</span><span>.</span><span>push</span><span>({ </span><span>name</span><span>:</span><span> </span><span>'user'</span><span>, </span><span>params</span><span>:</span><span> { </span><span>userId</span><span>:</span><span> </span><span>123</span><span> }})</span></div></div><div><div><div>7</div></div><div><span>// 带查询参数，变成 /register?plan=123</span></div></div><div><div><div>8</div></div><div><span>      </span><span>this</span><span>.</span><span>$router</span><span>.</span><span>push</span><span>({ </span><span>path</span><span>:</span><span> </span><span>'register'</span><span>, </span><span>query</span><span>:</span><span> { </span><span>plan</span><span>:</span><span> </span><span>'123'</span><span> }})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>push</code>方法其实和<code>&lt;router-link :to="..."&gt;</code>是等同的。
<strong>注意：push方法的跳转会向 history 栈添加一个新的记录，当我们点击浏览器的返回按钮时可以看到之前的页面。</strong></p></section><section><h4>this.$router.go()<a href="#thisroutergo"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 页面路由跳转 正数前进或者负数后退</span></div></div><div><div><div>2</div></div><div><span>this</span><span>.</span><span>$router</span><span>.</span><span>go</span><span>(</span><span>-</span><span>1</span><span>) </span><span>// 后退1步</span></div></div><div><div><div>3</div></div><div><span>this</span><span>.</span><span>$router</span><span>.</span><span>go</span><span>(</span><span>2</span><span>) </span><span>// 前进2步</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>this</span><span>.</span><span>$router</span><span>.</span><span>forward</span><span>() </span><span>//前进</span></div></div><div><div><div>6</div></div><div><span>this</span><span>.</span><span>$router</span><span>.</span><span>back</span><span>() </span><span>//后退</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>this.$router.replace()<a href="#thisrouterreplace"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//push方法会向 history 栈添加一个新的记录，而replace方法是替换当前的页面，</span></div></div><div><div><div>2</div></div><div><span><span>不会向</span><span> </span><span>history</span><span> </span><span>栈添加一个新的记录</span></span></div></div><div><div><div>3</div></div><div><span>&lt;</span><span>router-link</span><span> </span><span>to</span><span>=</span><span>"/05"</span><span> </span><span>replace</span><span>&gt;05&lt;/</span><span>router-link</span><span>&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 一般使用replace来做404页面</span></div></div><div><div><div>6</div></div><div><span>this</span><span>.</span><span>$router</span><span>.</span><span>replace</span><span>(</span><span>'/'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>缓存路由组件<a href="#缓存路由组件"><span>#</span></a></h2><ol>
<li>
<p>作用：让不展示的路由组件保持挂载，不被销毁。</p>
</li>
<li>
<p>具体编码：</p>
<p>这个 <code>include</code> 指的是组件名</p>
</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>keep-alive</span><span> </span><span>include</span><span>=</span><span>"News"</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>router-view</span><span>&gt;&lt;/</span><span>router-view</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;/</span><span>keep-alive</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>route生命周期钩子<a href="#route生命周期钩子"><span>#</span></a></h2><p>作用：路由组件所独有的两个钩子，用于捕获路由组件的激活状态。
具体名字：</p><ul>
<li><code>activated</code>路由组件被激活时触发。</li>
<li><code>deactivated</code>路由组件失活时触发。</li>
</ul><blockquote><p>这两个生命周期钩子需要配合前面的缓存路由组件使用（没有缓存路由组件不起效果）</p></blockquote></section>
<section><h2>路由守卫<a href="#路由守卫"><span>#</span></a></h2><ol>
<li>
<p>作用：对路由进行权限控制</p>
</li>
<li>
<p>分类：全局守卫、独享守卫、组件内守卫</p>
</li>
</ol><section><h3>全局守卫<a href="#全局守卫"><span>#</span></a></h3><p>全局前置守卫包含三个 <code>beforeEach</code> 前置守卫，<code>affterEach</code> 后置守卫，<code>beforeResolve</code> 解析守卫</p><section><h4>router.beforeEach<a href="#routerbeforeeach"><span>#</span></a></h4><p><code>router.beforeEach((to,from,next)=&gt;{})</code></p><p>回调函数中的参数，</p><ul>
<li>
<p><code>to</code>：进入到哪个路由去</p>
</li>
<li>
<p><code>from</code>：从哪个路由离开</p>
</li>
<li>
<p><code>next</code>：函数，决定是否展示你要看到的路由页面。</p>
<p><code>next</code>方法的调用参数。</p>
<ul>
<li><code>next()</code>: 进行管道中的下一个钩子。如果全部钩子执行完了，则导航的状态就是 <strong>confirmed</strong> (确认的)。</li>
<li><code>next(false)</code>: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮)，那么 URL 地址会重置到 <code>from</code> 路由对应的地址。</li>
<li><code>next('/')</code> 或者 <code>next({ path: '/' })</code>: 跳转到一个不同的地址。当前的导航被中断，然后进行一个新的导航。你可以向 <code>next</code> 传递任意位置对象，且允许设置诸如 <code>replace: true</code>、<code>name: 'home'</code> 之类的选项以及任何用在 <a href="https://router.vuejs.org/zh/api/#to" target="_blank"><code>router-link</code> 的 <code>to</code>prop</a> 或 <a href="https://router.vuejs.org/zh/api/#router-push" target="_blank"><code>router.push</code></a> 中的选项。</li>
<li><code>next(error)</code>: (2.4.0+) 如果传入 <code>next</code> 的参数是一个 <code>Error</code> 实例，则导航会被终止且该错误会被传递给 <a href="https://router.vuejs.org/zh/api/#router-onerror" target="_blank"><code>router.onError()</code></a> 注册过的回调。</li>
</ul>
</li>
</ul><p>从名字全局前置守卫不难理解，它是全局的，即对 整个单页应用（SPA） 中的所有路由都生效，所以当定义了全局的前置守卫，在进入每一个路由之前都会调用这个回调</p></section><section><h4>router.beforeResolve<a href="#routerbeforeresolve"><span>#</span></a></h4><p><code>router.beforeResolve((to, from, next) =&gt; {})</code></p><p>​	参数与<code>beforeEach</code>相同</p><ul>
<li><code>to</code>：进入到哪个路由去</li>
<li><code>from</code>：从哪个路由离开</li>
<li><code>next</code>：函数，决定是否展示你要看到的路由页面。</li>
</ul><p>他们两个的区别</p><ul>
<li>
<p>执行的时机不一样</p>
<p><code>beforeEach</code> 是在路由规则被循环之前执行
<code>beforResolve</code> 是在组件被解析之后调用</p>
</li>
<li>
<p>执行顺序</p>
<p><code>beforeEach</code> 要比 <code>beforResolve</code> 先执行</p>
</li>
</ul></section><section><h4>router.afterEach<a href="#routeraftereach"><span>#</span></a></h4><p><code>router.afterEach((to, from) =&gt; {})</code></p><p>​    只有两个参数，</p><ul>
<li><code>to</code>：进入到哪个路由去</li>
<li><code>from</code>：从哪个路由离开</li>
</ul><p>全局后置守卫是整个单页应用中每一次路由跳转后都会执行其中的回调。所以多用于路由跳转后的相应页面操作，并不像全局前置守卫那样会在回调中进行页面的重定向或跳转。</p></section><section><h4>案例<a href="#案例"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//全局前置守卫：初始化时执行、每次路由切换前执行</span></div></div><div><div><div>2</div></div><div><span><span>router</span><span>.</span></span><span>beforeEach</span><span><span>((</span><span>to</span><span>,</span><span>from</span><span>,</span><span>next</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span><span>(</span><span>to</span><span>.</span></span><span>meta</span><span>.</span><span>isAuth</span><span>){ </span><span>//判断当前路由是否需要进行权限控制</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span><span>(</span><span>localStorage</span><span>.</span></span><span>getItem</span><span>(</span><span>'school'</span><span>) </span><span>===</span><span> </span><span>'zhejiang'</span><span>){ </span><span>//权限控制的具体规则</span></div></div><div><div><div>5</div></div><div><span>      </span><span>next</span><span>() </span><span>//放行</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span><span>else</span><span>{</span></div></div><div><div><div>7</div></div><div><span>      </span><span>alert</span><span>(</span><span>'暂无权限查看'</span><span>)</span></div></div><div><div><div>8</div></div><div><span>      </span><span>// next({name:'guanyu'})</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span><span>else</span><span>{</span></div></div><div><div><div>11</div></div><div><span>    </span><span>next</span><span>() </span><span>//放行</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>})</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>//全局后置守卫：初始化时执行、每次路由切换后执行</span></div></div><div><div><div>16</div></div><div><span><span>router</span><span>.</span></span><span>afterEach</span><span><span>((</span><span>to</span><span>,</span><span>from</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>17</div></div><div><span>  </span><span>if</span><span><span>(</span><span>to</span><span>.</span></span><span>meta</span><span>.</span><span>title</span><span>){</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>document</span><span>.</span><span>title</span><span> </span><span>=</span><span><span> </span><span>to</span><span>.</span></span><span>meta</span><span>.</span><span>title</span><span> </span><span>//修改网页的title</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span><span>else</span><span>{</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>document</span><span>.</span><span>title</span><span> </span><span>=</span><span> </span><span>'vue_test'</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h3>独享守卫<a href="#独享守卫"><span>#</span></a></h3><p>独享守卫只有<code>beforeEnter</code>，并且写在<code>routes</code>的子路由中，仅对配置的一个路由生效</p><section><h4>beforeEnter<a href="#beforeenter"><span>#</span></a></h4><p><code>beforeEnter:(to,from,next)=&gt;{}</code></p><p>​	参数与<code>beforeEach</code>相同</p><ul>
<li><code>to</code>：进入到哪个路由去</li>
<li><code>from</code>：从哪个路由离开</li>
<li><code>next</code>：函数，决定是否展示你要看到的路由页面。</li>
</ul><p>​    与全局路由守卫用法一致，但是只能针对一个页面使用</p></section><section><h4>案例<a href="#案例-1"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const router =  new VueRouter({</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>routes: [</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>name:'xinwen',</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>path:'news',</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>component:News,</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>meta:{isAuth:true,title:'新闻'},</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>beforeEnter: (to, from, next) =&gt; {</span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>console.log('独享路由守卫',to,from)</span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>if(to.meta.isAuth){ //判断是否需要鉴权</span></div></div><div><div><div>11</div></div><div><span><span>                    </span></span><span>if(localStorage.getItem('school')==='atguigu'){</span></div></div><div><div><div>12</div></div><div><span><span>                        </span></span><span>next()</span></div></div><div><div><div>13</div></div><div><span><span>                    </span></span><span>}else{</span></div></div><div><div><div>14</div></div><div><span><span>                        </span></span><span>alert('学校名不对，无权限查看！')</span></div></div><div><div><div>15</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>                </span></span><span>}else{</span></div></div><div><div><div>17</div></div><div><span><span>                    </span></span><span>next()</span></div></div><div><div><div>18</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>22</div></div><div><span>)}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h3>组件内的守卫<a href="#组件内的守卫"><span>#</span></a></h3><p>组件内的守卫包含三个 <code>beforeRouterEnter</code> ，<code>beforeRouterUpdated</code>， <code>beforeRouterLeave</code></p></section><section><h3>beforeRouterEnter<a href="#beforerouterenter"><span>#</span></a></h3><p><code>beforeRouteEnter:(to,from,next)=&gt;{}</code></p><p>参数与全局路由守卫一致，触发在进入组件之前。当守卫执行前，组件实例还没被创建，故无法访问 <code>this</code></p><p>项目需要在进入某个页面前，判断从特定页面进来时，做某些处理。例如：只有从详情页回到列表页需要重新调接口。此时，用到了<code>beforeRouteEnter</code>方法。</p><p><strong>注意</strong>：在内部获取不到外部的<code>this</code>，方法、变量等都获取不到。但<code>vm</code>可以获取到<code>method</code>中的方法以及变量</p></section><section><h3>beforeRouterUpdated<a href="#beforerouterupdated"><span>#</span></a></h3><p><code>beforeRouterUpdated:(to,from,next)=&gt;{}</code></p><p>参数与全局路由守卫一致</p><p>在当前路由改变，但是该组件被复用时调用，即路由路径不变，调用的是同个界面，即组件被连续复用的时候，会调用到<code>beforeRouteUpdate</code>触发</p></section><section><h3>beforeRouterLeave<a href="#beforerouterleave"><span>#</span></a></h3><p><code>beforeRouterLeave:(to,from,next)=&gt;{}</code></p><p>参数与全局路由守卫一致，导航离开该组件的对应路由时调用</p><p>在页面离开时做的操作，最常见的场景：用户修改了页面某些字段，还没有保存就要离开当前页面。此时在页面离开前需要给用户提示</p><p><strong>注意</strong>：此时函数内部可以访问到<code>this</code>，执行完要做的操作后，必须写 <code>next()</code>，否则页面不会跳转</p></section><section><h3>案例<a href="#案例-2"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>name</span><span>:</span><span> </span><span>'About'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>beforeCreate</span><span>() {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'beforeCreate'</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>mounted</span><span>() {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'mounted'</span><span>)</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span>  </span><span>beforeDestroy</span><span>() {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'beforeDestroy'</span><span>)</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>//通过路由规则，进入该组件时被调用</span></div></div><div><div><div>15</div></div><div><span>  </span><span>beforeRouteEnter</span><span><span>(</span><span>to</span><span>, </span><span>from</span><span>, </span><span>next</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'beforeRouteEnter'</span><span>, </span><span>to</span><span>, </span><span>from</span><span>)</span></div></div><div><div><div>17</div></div><div><span>    </span><span>if</span><span><span> (</span><span>to</span><span>.</span></span><span>meta</span><span>.</span><span>isAuth</span><span>) {</span></div></div><div><div><div>18</div></div><div><span>      </span><span>//判断是否需要鉴权</span></div></div><div><div><div>19</div></div><div><span>      </span><span>if</span><span><span> (</span><span>localStorage</span><span>.</span></span><span>getItem</span><span>(</span><span>'school'</span><span>) </span><span>===</span><span> </span><span>'xuexiao'</span><span>) {</span></div></div><div><div><div>20</div></div><div><span>        </span><span>next</span><span>()</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>alert</span><span>(</span><span>'学校名不对，无权限查看！'</span><span>)</span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>25</div></div><div><span>      </span><span>next</span><span>()</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>  </span><span>//通过路由规则，离开该组件时被调用</span></div></div><div><div><div>30</div></div><div><span>  </span><span>beforeRouteLeave</span><span><span>(</span><span>to</span><span>, </span><span>from</span><span>, </span><span>next</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'beforeRouteLeave'</span><span>, </span><span>to</span><span>, </span><span>from</span><span>)</span></div></div><div><div><div>32</div></div><div><span>    </span><span>next</span><span>()</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>//输出：</span></div></div><div><div><div>37</div></div><div><span>// beforeRouteEnter</span></div></div><div><div><div>38</div></div><div><span>// beforeCreate</span></div></div><div><div><div>39</div></div><div><span>// mounted</span></div></div><div><div><div>40</div></div><div><span>// beforeRouteLeave</span></div></div><div><div><div>41</div></div><div><span>// beforeDestroy</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>路由器的两种工作模式<a href="#路由器的两种工作模式"><span>#</span></a></h2><p>路由器的两种工作模式分别为<code>hash</code>模式和<code>history</code>模式，默认是<code>hash</code>模式。</p><ol>
<li>
<p>对于一个url来说，什么是hash值？—— #及其后面的内容就是hash值。</p>
</li>
<li>
<p>hash值不会包含在 HTTP 请求中，即：hash值不会带给服务器。</p>
</li>
</ol><section><h3>设置模式<a href="#设置模式"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span>  </span><span>new</span><span> </span><span>VueRouter</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>mode</span><span>:</span><span>'history'</span><span>, </span><span>// 默认不写mode 是hash</span></div></div><div><div><div>3</div></div><div><span>  </span><span>routes</span><span>:</span><span>[{}],</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>hash模式<a href="#hash模式"><span>#</span></a></h3><p>路由器默认的工作模式是<code>hash</code>模式。因此我们看到的是</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://localhost:8080/#/</span></div></div><div><div><div>2</div></div><div><span>http://localhost:8080/#/about</span></div></div><div><div><div>3</div></div><div><span>http://localhost:8080/#/home</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>地址中永远带着#号，不美观 。</li>
<li>若以后将地址通过第三方手机app分享，若app校验严格，则地址会被标记为不合法。</li>
<li>兼容性较好，<code>hash</code>模式所有浏览器天生支持</li>
</ol></section><section><h3>history模式<a href="#history模式"><span>#</span></a></h3><p>路由器工作模式为<code>history</code>模式时，我们看到的是</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://localhost:8080/</span></div></div><div><div><div>2</div></div><div><span>http://localhost:8080/about</span></div></div><div><div><div>3</div></div><div><span>http://localhost:8080/home</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>地址干净，美观 。</li>
<li><code>history</code>模式，它内部使用的是<code>h5</code>中提供的<code>api</code>，兼容性没有<code>hash</code>模式的兼容性好</li>
<li>应用部署上线时需要后端人员支持，解决刷新页面服务端404的问题。</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-jiao-shou-jia/</id>
      <title type="text">Vue 脚手架</title>
      <published>2022-10-07T08:54:08.000Z</published>
      <updated>2022-10-07T08:54:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-jiao-shou-jia/"/>
      <summary type="text">使用前置： 第一步(没有安装过的执行)：全局安装 @vue/cli npm install g @vue/cli 第二步：切换到要创建项目的目录，然后使用命令创建项目 vue create xxxxx 第三步：启动项目 npm run serve</summary>
      <content type="html"><![CDATA[<blockquote><p>使用前置：</p><p>第一步(没有安装过的执行)：全局安装 @vue/cli</p><p>npm install -g @vue/cli</p><p>第二步：切换到要创建项目的目录，然后使用命令创建项目</p><p>vue create xxxxx</p><p>第三步：启动项目</p><p>npm run serve</p></blockquote>
<section><h2>脚手架文件结构<a href="#脚手架文件结构"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>├── node_modules</span></div></div><div><div><div>2</div></div><div><span>├── public</span></div></div><div><div><div>3</div></div><div><span>│   ├── favicon.ico: 页签图标</span></div></div><div><div><div>4</div></div><div><span>│   └── index.html: 主页面</span></div></div><div><div><div>5</div></div><div><span>├── src</span></div></div><div><div><div>6</div></div><div><span>│   ├── assets: 存放静态资源</span></div></div><div><div><div>7</div></div><div><span>│   │   └── logo.png</span></div></div><div><div><div>8</div></div><div><span>│   │── component: 存放组件</span></div></div><div><div><div>9</div></div><div><span>│   │   └── HelloWorld.vue</span></div></div><div><div><div>10</div></div><div><span>│   │── App.vue: 汇总所有组件</span></div></div><div><div><div>11</div></div><div><span>│   │── main.js: 入口文件</span></div></div><div><div><div>12</div></div><div><span>├── .gitignore: git版本管制忽略的配置</span></div></div><div><div><div>13</div></div><div><span>├── babel.config.js: babel的配置文件</span></div></div><div><div><div>14</div></div><div><span>├── package.json: 应用包配置文件</span></div></div><div><div><div>15</div></div><div><span>├── README.md: 应用描述文件</span></div></div><div><div><div>16</div></div><div><span>├── package-lock.json：包版本控制文件</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>脚手架demo<a href="#脚手架demo"><span>#</span></a></h2><section><h3>components:<a href="#components"><span>#</span></a></h3><p>就直接把单文件组件的 School.vue 和 Student.vue 两个文件直接拿来用，不需要修改。</p></section><section><h3>App.vue:<a href="#appvue"><span>#</span></a></h3><p>引入这两个组件，注册一下这两个组件，再使用。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"app"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>img</span><span> </span><span>alt</span><span>=</span><span>"Vue logo"</span><span> </span><span>src</span><span>=</span><span>"./assets/logo.png"</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>Student</span><span>&gt;&lt;/</span><span>Student</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>School</span><span>&gt;&lt;/</span><span>School</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span>import</span><span> </span><span>School</span><span> </span><span>from</span><span> </span><span>'./components/School.vue'</span></div></div><div><div><div>11</div></div><div><span>import</span><span> </span><span>Student</span><span> </span><span>from</span><span> </span><span>'./components/Student.vue'</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>14</div></div><div><span>  </span><span>name</span><span>:</span><span> </span><span>'App'</span><span>,</span></div></div><div><div><div>15</div></div><div><span>  </span><span>components</span><span>:</span><span> {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>School</span><span>,</span></div></div><div><div><div>17</div></div><div><span>    </span><span>Student</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>&lt;</span><span>style</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span>#app</span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>font-family: Avenir, </span><span>Helvetica</span><span>, </span><span>Arial</span><span>, </span><span>sans-serif</span><span>;</span></div></div><div><div><div>25</div></div><div><span>  </span><span>-webkit-font-smoothing</span><span><span>: </span><span>antialiased</span><span>;</span></span></div></div><div><div><div>26</div></div><div><span>  </span><span>-moz-osx-font-smoothing</span><span><span>: </span><span>grayscale</span><span>;</span></span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>text-align: </span><span>center</span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>color: </span><span>#</span><span>2c3e50</span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>margin-top: </span><span><span>60</span><span>px</span></span><span>;</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div><div><div><div>31</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>main.js:<a href="#mainjs"><span>#</span></a></h3><p>入口文件</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Vue</span><span> </span><span>from</span><span> </span><span>'vue'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>App</span><span> </span><span>from</span><span> </span><span>'./App.vue'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>Vue</span><span>.</span></span><span>config</span><span>.</span><span>productionTip</span><span> </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>7</div></div><div><span>  </span><span>render</span><span>:</span><span><span> </span><span>h</span><span> </span></span><span>=&gt;</span><span> </span><span>h</span><span><span>(</span><span>App</span><span>),</span></span></div></div><div><div><div>8</div></div><div><span>}).</span><span>$mount</span><span>(</span><span>'#app'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>接下来就要详细讲解 main.js 中的 render 函数</p></section></section>
<section><h2>render函数<a href="#render函数"><span>#</span></a></h2><section><h3>基本介绍<a href="#基本介绍"><span>#</span></a></h3><p>之前的写法是这样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>App</span><span> </span><span>from</span><span> </span><span>'./App.vue'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>4</div></div><div><span>  </span><span>el</span><span>:</span><span>'#root'</span><span>,</span></div></div><div><div><div>5</div></div><div><span>  </span><span>template</span><span>:</span><span>`&lt;App&gt;&lt;/App&gt;`</span><span>,</span></div></div><div><div><div>6</div></div><div><span>  </span><span>components</span><span>:</span><span><span>{</span><span>App</span><span>},</span></span></div></div><div><div><div>7</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果这样子写，运行的话会引发如下的报错</p><p></p><figure><img src="/images/vue/vue_%E8%84%9A%E6%89%8B%E6%9E%B6_render%E5%87%BD%E6%95%B0.png" alt="vue_脚手架_render函数" /><figcaption>vue_脚手架_render函数</figcaption></figure><p></p><p>报错的意思是，是在使用运行版本的 vue ，没有模板解析器。</p><p>从上面的小知识可以知道，我们引入的 vue 不是完整版的，是残缺的（为了减小vue的大小）。所以残缺的<code>vue.js</code> 只有通过 <code>render</code> 函数才能把项目给跑起来。</p><p>来解析一下<code>render</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// render最原始写的方式</span></div></div><div><div><div>2</div></div><div><span>// render是个函数，还能接收到参数a</span></div></div><div><div><div>3</div></div><div><span>// 这个 createElement 很关键，是个回调函数</span></div></div><div><div><div>4</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>5</div></div><div><span>  </span><span>render</span><span><span>(</span><span>createElement</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>typeof</span><span><span> </span><span>createElement</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span>      </span><span>// 这个 createElement 回调函数能创建元素</span></div></div><div><div><div>8</div></div><div><span>      </span><span>// 因为残缺的vue 不能解析 template，所以render就来帮忙解决这个问题</span></div></div><div><div><div>9</div></div><div><span>      </span><span>// createElement 能创建具体的元素</span></div></div><div><div><div>10</div></div><div><span>      </span><span>return</span><span> </span><span>createElement</span><span>(</span><span>'h1'</span><span>, </span><span>'hello'</span><span>)</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>}).</span><span>$mount</span><span>(</span><span>'#app'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="/images/vue/vue_%E8%84%9A%E6%89%8B%E6%9E%B6_render%E5%87%BD%E6%95%B02.png" alt="vue_脚手架_render函数2" /><figcaption>vue_脚手架_render函数2</figcaption></figure><p></p><p>因为 <code>render</code> 函数内并没有用到 <code>this</code>，所以可以简写成箭头函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// render: h =&gt; h(App),</span></div></div><div><div><div>3</div></div><div><span>  </span><span>render</span><span>:</span><span><span> (</span><span>createElement</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span> </span><span>createElement</span><span><span>(</span><span>App</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}).</span><span>$mount</span><span>(</span><span>'#app'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>再简写：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// render: h =&gt; h(App),</span></div></div><div><div><div>3</div></div><div><span>  </span><span>render</span><span>:</span><span><span> </span><span>createElement</span><span> </span></span><span>=&gt;</span><span> </span><span>createElement</span><span><span>(</span><span>App</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span>}).</span><span>$mount</span><span>(</span><span>'#app'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>最后把 <code>createElement</code> 换成 <code>h</code> 就完事了。</p></section><section><h3>总结<a href="#总结"><span>#</span></a></h3><p>对象内写方法最原始的：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>obj</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'aaa'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>work</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>salary</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>        </span><span>return</span><span> </span><span>'工资'</span><span> </span><span>+</span><span><span> </span><span>salary</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>ES6 简化版：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>obj</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'aaa'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>work</span><span><span>(</span><span>salary</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>        </span><span>return</span><span> </span><span>'工资'</span><span> </span><span>+</span><span><span> </span><span>salary</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>箭头函数简化版:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>obj</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'aaa'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>work</span><span>:</span><span><span> (</span><span>salary</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>return</span><span> </span><span>'工资'</span><span> </span><span>+</span><span><span> </span><span>salary</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>箭头函数再简化（最终版）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 只有一个参数就可以把圆括号去了，函数体内部只有一个 return 就可以把大括号去掉，return去掉</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>obj</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'aaa'</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>work</span><span>:</span><span><span> </span><span>salary</span><span> </span></span><span>=&gt;</span><span> </span><span>'工资'</span><span> </span><span>+</span><span><span> </span><span>salary</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样就可以理解 <code>render</code> 函数的简写方式了。</p><p>来个不同版本 vue 的区别</p><ul>
<li><code>vue.js</code>与<code>vue.runtime.xxx.js</code>的区别：
<ul>
<li><code>vue.js</code>是完整版的Vue，包含：核心功能+模板解析器。</li>
<li><code>vue.runtime.xxx.js</code>是运行版的Vue，只包含：核心功能；没有模板解析器。</li>
</ul>
</li>
<li>因为<code>vue.runtime.xxx.js</code>没有模板解析器，所以不能使用<code>template</code>配置项，需要使用<code>render</code>函数接收到的<code>createElement</code>函数去指定具体内容。</li>
</ul></section></section>
<section><h2>修改脚手架的默认配置<a href="#修改脚手架的默认配置"><span>#</span></a></h2><ul>
<li>使用vue inspect &gt; output.js可以查看到Vue脚手架的默认配置。</li>
<li>使用vue.config.js可以对脚手架进行个性化定制，详情见：<a href="https://cli.vuejs.org/zh" target="_blank">https://cli.vuejs.org/zh</a></li>
</ul></section>
<section><h2>脚手架中的index.html<a href="#脚手架中的indexhtml"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!</span><span>DOCTYPE</span><span> </span><span>html</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>html</span><span> </span><span>lang</span><span>=</span><span>""</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;</span><span>head</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>meta</span><span> </span><span>charset</span><span>=</span><span>"utf-8"</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>&lt;!-- 针对IE浏览器的一个特殊配置，含义是让IE浏览器以最高的渲染级别渲染页面 --&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;</span><span>meta</span><span> </span><span>http-equiv</span><span>=</span><span>"X-UA-Compatible"</span><span> </span><span>content</span><span>=</span><span>"IE=edge"</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>  </span><span>&lt;!-- 开启移动端的理想视口 --&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;</span><span>meta</span><span> </span><span>name</span><span>=</span><span>"viewport"</span><span> </span><span>content</span><span>=</span><span>"width=device-width,initial-scale=1.0"</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>&lt;!-- 配置页签图标 --&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;</span><span>link</span><span> </span><span>rel</span><span>=</span><span>"icon"</span><span> </span><span>href</span><span>=</span><span>"</span><span>&lt;</span><span>%= BASE_URL %&gt;favicon.ico"</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>  </span><span>&lt;!-- 引入第三方样式 --&gt;</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>&lt;</span><span>link</span><span> </span><span>rel</span><span>=</span><span>"stylesheet"</span><span> </span><span>href</span><span>=</span><span>"</span><span>&lt;</span><span>%= BASE_URL %&gt;css/bootstrap.css"</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span>  </span><span>&lt;!-- 配置网页标题 --&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;</span><span>title</span><span>&gt;硅谷系统&lt;/</span><span>title</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>&lt;/</span><span>head</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>&lt;</span><span>body</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>&lt;!-- 当浏览器不支持js时noscript中的元素就会被渲染 --&gt;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>&lt;</span><span>noscript</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>&lt;</span><span>strong</span><span>&gt;We're sorry but </span><span>&lt;</span><span>%= htmlWebpackPlugin.options.title %&gt; doesn't work properly without JavaScript enabled. Please enable it to continue.&lt;/</span><span>strong</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>&lt;/</span><span>noscript</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>&lt;!-- 容器 --&gt;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"app"</span><span>&gt;&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>&lt;!-- built files will be auto injected --&gt;</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>&lt;/</span><span>body</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span>&lt;/</span><span>html</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/zu-jian-jian-tong-xin/</id>
      <title type="text">组件间通信</title>
      <published>2022-10-07T06:25:42.000Z</published>
      <updated>2022-10-07T06:25:42.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/zu-jian-jian-tong-xin/"/>
      <summary type="text">一个组件里面引入另外一个组件，此时构成了一种“父子关系”，当前组件为“父”，引入的组件为“子”，如当前组件（父），在父组件中通过 :message 向子组件通信。</summary>
      <content type="html"><![CDATA[<section><h2>props<a href="#props"><span>#</span></a></h2><section><h3>方式<a href="#方式"><span>#</span></a></h3><p>一个组件里面引入另外一个组件，此时构成了一种“父子关系”，当前组件为“父”，引入的组件为“子”，如当前组件（父），在父组件中通过 <code>:message</code>向子组件通信。</p><p>父组件 <code>Parent.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"parent-box"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>&lt;</span><span>div</span><span>&gt;我是父组件&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>&lt;</span><span>div</span><span>&gt;{{ message }}&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>&lt;</span><span>children</span><span> </span><span>:message</span><span>=</span><span>"toChildrenMsg"</span><span>&gt;&lt;/</span><span>children</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>import</span><span> </span><span>Children</span><span> </span><span>from</span><span> </span><span>'./Children.vue'</span><span> </span><span>//当前页引入子组件</span></div></div><div><div><div>13</div></div><div><span>    </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'Parent'</span><span>,</span></div></div><div><div><div>15</div></div><div><span>        </span><span>components</span><span>:</span><span> {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>Children</span><span>,</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span>        </span><span>data</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>            </span><span>return</span><span> {</span></div></div><div><div><div>20</div></div><div><span>                </span><span>message</span><span>:</span><span> </span><span>'我是父组件的内容'</span><span>,</span></div></div><div><div><div>21</div></div><div><span>                </span><span>toChildrenMsg</span><span>:</span><span> </span><span>'从父组件传过到子组件的内容'</span><span>,</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>子组件 <code>Children.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"children-box"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>&lt;</span><span>div</span><span>&gt;我是子组件&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>&lt;</span><span>div</span><span>&gt;{{ message }}&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'Children'</span><span>,</span></div></div><div><div><div>13</div></div><div><span>        </span><span>props</span><span>:</span><span> {</span></div></div><div><div><div>14</div></div><div><span>            </span><span>message</span><span>:</span><span> {</span></div></div><div><div><div>15</div></div><div><span>                </span><span>type</span><span>:</span><span> </span><span>String</span><span>, </span><span>//类型判断</span></div></div><div><div><div>16</div></div><div><span>                </span><span>default</span><span>:</span><span> </span><span>''</span><span>, </span><span>//默认值</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在子组件通过<code>props</code>进行接收，注意子组件<code>props</code>里面接收的对象名称必须与父组件中在子组件绑定的名称一致，当前例子为<code>message</code>，可以在组件中<code>this.message</code>的方式使用<code>props</code>里面的值。</p></section><section><h3>优缺点<a href="#优缺点"><span>#</span></a></h3><ol>
<li>
<p>此方式用于父组件向子组件传递数据</p>
</li>
<li>
<p>所有标签属性都会成为组件对象的属性，模板组件可以直接引用</p>
</li>
<li>
<p>问题:</p>
<p>a. 如果需要向非子后代传递数据必须多层逐层传递</p>
<p>b. 兄弟组件间也不能直接 props 通信，必须借助父组件才可以</p>
</li>
</ol></section></section>
<section><h2>自定义事件<a href="#自定义事件"><span>#</span></a></h2><p>组件自定义事件是一种组件间通信的方式，适用于：<strong>子组件 ===&gt; 父组件</strong></p><p><strong>使用场景</strong></p><p>A是父组件，B是子组件，B想给A传数据，那么就要在A中给B绑定自定义事件（<span>事件的回调在A中</span>）。</p><section><h3>第一种绑定方式<a href="#第一种绑定方式"><span>#</span></a></h3><p>在父组件中：<code>&lt;Demo @hello="test"/&gt;</code>或 <code>&lt;Demo v-on:hello="test"/&gt;</code></p><p><code>App.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"app"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>&lt;!-- 通过父组件给子组件绑定一个自定义事件实现：子给父传递数据（第一种写法，使用@或v-on） --&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>Student</span><span> @</span><span>atguigu</span><span>=</span><span>"</span><span>getStudentName</span><span>"</span><span>/&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>import</span><span> </span><span>Student</span><span> </span><span>from</span><span> </span><span>'./components/Student'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>name</span><span>:</span><span>'App'</span><span>,</span></div></div><div><div><div>13</div></div><div><span>    </span><span>components</span><span>:</span><span>{</span><span>Student</span><span>},</span></div></div><div><div><div>14</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>15</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>msg</span><span>:</span><span>'你好啊！'</span><span>,</span></div></div><div><div><div>17</div></div><div><span>        </span><span>studentName</span><span>:</span><span>''</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>20</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>21</div></div><div><span>      </span><span>getStudentName</span><span><span>(</span><span>name</span><span>,</span></span><span>...</span><span><span>params</span><span>){</span></span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'App收到了学生名：'</span><span>,</span><span>name</span><span>,</span><span>params</span><span>)</span></div></div><div><div><div>23</div></div><div><span>        </span><span>this</span><span>.</span><span>studentName</span><span> </span><span>=</span><span> </span><span>name</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>Student.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"student"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"sendStudentlName"</span><span>&gt;把学生名给App&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>name</span><span>:</span><span>'Student'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>name</span><span>:</span><span>'张三'</span><span>,</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>16</div></div><div><span>      </span><span>sendStudentlName</span><span>(){</span></div></div><div><div><div>17</div></div><div><span>        </span><span>//触发Student组件实例身上的atguigu事件</span></div></div><div><div><div>18</div></div><div><span>        </span><span>this</span><span>.</span><span>$emit</span><span>(</span><span>'atguigu'</span><span>,</span><span>this</span><span>.</span><span>name</span><span>,</span><span>666</span><span>,</span><span>888</span><span>,</span><span>900</span><span>)</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>&lt;</span><span>style</span><span> </span><span>lang</span><span>=</span><span>"less"</span><span> </span><span>scoped</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span>  </span><span>.student</span><span>{</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>background-color: </span><span>pink</span><span>;</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>padding: </span><span><span>5</span><span>px</span></span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>margin-top: </span><span><span>30</span><span>px</span></span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>当子组件<code>Student.vue</code>触发点击事件时，<code>Student.vue</code>中<code>$emit</code>执行，触发 <code>App.vue</code>组件上<code>Student</code>组件实例身上的<code>atguigu</code>事件，将<code>this.$emit('atguigu',this.name,666,888,900)</code>中，需要的参数交给<code>App.vue</code>以实现子组件向父组件传参。</p></section><section><h3>第二种绑定方式<a href="#第二种绑定方式"><span>#</span></a></h3><p>在父组件中：使用 <code>this.$refs.xxx.$on()</code></p><p>这样写起来更灵活，比如可以加定时器啥的。</p><p><code>App.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"app"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>&lt;!-- 通过父组件给子组件绑定一个自定义事件实现：子给父传递数据（第二种写法，使用ref） --&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>Student</span><span> </span><span>ref</span><span>=</span><span>"student"</span><span>/&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>import</span><span> </span><span>Student</span><span> </span><span>from</span><span> </span><span>'./components/Student'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>name</span><span>:</span><span>'App'</span><span>,</span></div></div><div><div><div>13</div></div><div><span>    </span><span>components</span><span>:</span><span>{</span><span>Student</span><span>},</span></div></div><div><div><div>14</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>15</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>studentName</span><span>:</span><span>''</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>19</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>20</div></div><div><span>      </span><span>getStudentName</span><span><span>(</span><span>name</span><span>,</span></span><span>...</span><span><span>params</span><span>){</span></span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'App收到了学生名：'</span><span>,</span><span>name</span><span>,</span><span>params</span><span>)</span></div></div><div><div><div>22</div></div><div><span>        </span><span>this</span><span>.</span><span>studentName</span><span> </span><span>=</span><span> </span><span>name</span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>25</div></div><div><span>    </span><span>mounted</span><span>() {</span></div></div><div><div><div>26</div></div><div><span>      </span><span>this</span><span>.</span><span>$refs</span><span>.</span><span>student</span><span>.</span><span>$on</span><span>(</span><span>'atguigu'</span><span>,</span><span>this</span><span>.</span><span>getStudentName</span><span>) </span><span>//绑定自定义事件</span></div></div><div><div><div>27</div></div><div><span>      </span><span>// this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件（一次性）</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>Student.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"student"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"sendStudentlName"</span><span>&gt;把学生名给App&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>name</span><span>:</span><span>'Student'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>name</span><span>:</span><span>'张三'</span><span>,</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>16</div></div><div><span>      </span><span>sendStudentlName</span><span>(){</span></div></div><div><div><div>17</div></div><div><span>        </span><span>//触发Student组件实例身上的atguigu事件</span></div></div><div><div><div>18</div></div><div><span>        </span><span>this</span><span>.</span><span>$emit</span><span>(</span><span>'atguigu'</span><span>,</span><span>this</span><span>.</span><span>name</span><span>,</span><span>666</span><span>,</span><span>888</span><span>,</span><span>900</span><span>)</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上述实例代码中，在父组件 <code>App.vue</code> 中为 <code>Student</code>组件取别名 <code>&lt;Student ref="student"/&gt;</code>，通过<code>this.$refs.student.$on('atguigu',this.getStudentName)</code>绑定自定义事件 <code>atguigu</code> ，然后在子组件<code>Student</code>中触发事件传参</p><blockquote><p>注意：通过<code>this.$refs.xxx.$on('atguigu',回调)</code>绑定自定义事件时，回调<span>要么配置在methods中</span>，<span>要么用箭头函数</span>，否则this指向会出问题！</p></blockquote></section><section><h3>解绑自定义事件<a href="#解绑自定义事件"><span>#</span></a></h3><p>解绑自定义事件，<code>this.$off('atguigu')</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>this.$off('atguigu') //解绑一个自定义事件</span></div></div><div><div><div>2</div></div><div><span>// this.$off(['atguigu','demo']) //解绑多个自定义事件</span></div></div><div><div><div>3</div></div><div><span>// this.$off() //解绑所有的自定义事件</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在绑定自定义事件后，如果组价执行完毕，在<code>beforeDestroy</code>钩子中解绑自定义事件，防止冲突</p></section></section>
<section><h2>全局事件总线<a href="#全局事件总线"><span>#</span></a></h2><p>全局事件总线是一种组件间通信的方式，适用于<strong>任意组件间通信</strong>。</p><section><h3>安装全局事件总线<a href="#安装全局事件总线"><span>#</span></a></h3><p>在<code>main.js</code>中，创建 <code>Vue</code>实例时，使用<code>Vue.prototype.$bus = this</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new Vue({</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>el:'#app',</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>render: h =&gt; h(App),</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>beforeCreate() {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Vue.prototype.$bus = this //安装全局事件总线，$bus就是当前应用的vm</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>使用全局事件总线<a href="#使用全局事件总线"><span>#</span></a></h3><ol>
<li>
<p>接收数据：A组件想接收数据，则在A组件中给<code>$bus</code>绑定自定义事件，事件的<span>回调留在A组件自身。</span></p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>methods</span><span>(){</span></div></div><div><div><div>2</div></div><div><span>  </span><span>demo</span><span><span>(</span><span>data</span><span>){}</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>//</span></div></div><div><div><div>5</div></div><div><span>mounted</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>this</span><span>.</span><span>$bus</span><span>.</span><span>$on</span><span>(</span><span>'xxxx'</span><span>,</span><span>this</span><span>.</span><span>demo</span><span>)</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>提供数据：<code>this.$bus.$emit('xxxx',数据)</code></p>
</li>
<li>
<p>最好在<code>beforeDestroy</code>钩子中，用<code>$off</code>去解绑<span>当前组件所用到的</span>事件。</p>
</li>
</ol></section><section><h3>实例代码<a href="#实例代码"><span>#</span></a></h3><p><code>School.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"school"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学校名称：{{name}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学校地址：{{address}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>name</span><span>:</span><span>'School'</span><span>,</span></div></div><div><div><div>11</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>name</span><span>:</span><span>'尚硅谷'</span><span>,</span></div></div><div><div><div>14</div></div><div><span>        </span><span>address</span><span>:</span><span>'北京'</span><span>,</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span>        </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>18</div></div><div><span>            </span><span>demo</span><span><span>(</span><span>data</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span><span>                </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'我是School组件，收到了数据'</span><span>,</span><span>data</span><span>)</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>    </span><span>mounted</span><span>() {</span></div></div><div><div><div>23</div></div><div><span>            </span><span>// 在接收数据的组件上绑定事件</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>this.$bus.$on(</span><span>'hello'</span><span>,this.demo)</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>26</div></div><div><span>    </span><span>beforeDestroy</span><span>() {</span></div></div><div><div><div>27</div></div><div><span>            </span><span>// 解绑事件</span></div></div><div><div><div>28</div></div><div><span>      </span><span>this</span><span>.</span><span>$bus</span><span>.</span><span>$off</span><span>(</span><span>'hello'</span><span>)</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>31</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>Student.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"student"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学生姓名：{{name}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学生性别：{{sex}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"sendStudentName"</span><span>&gt;把学生名给School组件&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>name</span><span>:</span><span>'Student'</span><span>,</span></div></div><div><div><div>12</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>name</span><span>:</span><span>'张三'</span><span>,</span></div></div><div><div><div>15</div></div><div><span>        </span><span>sex</span><span>:</span><span>'男'</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>sendStudentName</span><span>(){</span></div></div><div><div><div>20</div></div><div><span>                </span><span>// 触发想接收数据的组件上的事件，传入想发送的数据</span></div></div><div><div><div>21</div></div><div><span>        </span><span>this</span><span>.</span><span>$bus</span><span>.</span><span>$emit</span><span>(</span><span>'hello'</span><span>,</span><span>this</span><span>.</span><span>name</span><span>)</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p></p><figure><img src="/images/vue/vue_%E7%BB%84%E4%BB%B6%E9%97%B4%E9%80%9A%E4%BF%A1_%E5%85%A8%E5%B1%80%E4%BA%8B%E4%BB%B6%E6%80%BB%E7%BA%BF.png" alt="vue_组件间通信_全局事件总线" /><figcaption>vue_组件间通信_全局事件总线</figcaption></figure><p></p></section></section>
<section><h2>消息订阅与发布<a href="#消息订阅与发布"><span>#</span></a></h2><p>一种组件间通信的方式，适用于<span>任意组件间通信</span>。</p><section><h3>使用方法<a href="#使用方法"><span>#</span></a></h3><ol>
<li>
<p>安装pubsub：<code>npm i pubsub-js</code></p>
</li>
<li>
<p>引入: <code>import pubsub from 'pubsub-js'</code></p>
</li>
<li>
<p>接收数据：A组件想接收数据，则在A组件中订阅消息，订阅的<span>回调留在A组件自身。</span></p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>PubSub</span><span> </span><span>from</span><span> </span><span>'pubsub-js'</span><span> </span><span>//引入</span></div></div><div><div><div>2</div></div><div><span><span>PubSub</span><span>.</span></span><span>subscribe</span><span>(</span><span>'delete'</span><span>, </span><span>function</span><span><span>(</span><span>msg</span><span>, </span><span>data</span><span>){ }) </span></span><span>//订阅</span></div></div><div><div><div>3</div></div><div><span><span>PubSub</span><span>.</span></span><span>publish</span><span>(</span><span>'delete'</span><span><span>, </span><span>data</span><span>) </span></span><span>//发布消息</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>提供数据：<code>pubsub.publish('xxx',数据)</code></p>
</li>
<li>
<p>最好在beforeDestroy钩子中，用<code>PubSub.unsubscribe(pid)</code>去<span>取消订阅。</span></p>
</li>
</ol></section><section><h3>实例代码<a href="#实例代码-1"><span>#</span></a></h3><p>订阅消息<code>School.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"school"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学校名称：{{name}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学校地址：{{address}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>import</span><span> </span><span>pubsub</span><span> </span><span>from</span><span> </span><span>'pubsub-js'</span></div></div><div><div><div>10</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>name</span><span>:</span><span>'School'</span><span>,</span></div></div><div><div><div>12</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>name</span><span>:</span><span>'尚硅谷'</span><span>,</span></div></div><div><div><div>15</div></div><div><span>        </span><span>address</span><span>:</span><span>'北京'</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span>    </span><span>mounted</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>/* this.$bus.$on('hello',(data)=&gt;{</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>console.log('我是School组件，收到了数据',data)</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>}) */</span></div></div><div><div><div>22</div></div><div><span>      </span><span>this</span><span>.</span><span>pubId</span><span> </span><span>=</span><span><span> </span><span>pubsub</span><span>.</span></span><span>subscribe</span><span>(</span><span>'hello'</span><span><span>,(</span><span>msgName</span><span>,</span><span>data</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>)</span></div></div><div><div><div>24</div></div><div><span>        </span><span>// console.log('有人发布了hello消息，hello消息的回调执行了',msgName,data)</span></div></div><div><div><div>25</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>27</div></div><div><span>    </span><span>beforeDestroy</span><span>() {</span></div></div><div><div><div>28</div></div><div><span>      </span><span>// this.$bus.$off('hello')</span></div></div><div><div><div>29</div></div><div><span><span>      </span></span><span>pubsub</span><span>.</span><span>unsubscribe</span><span>(</span><span>this</span><span>.</span><span>pubId</span><span>)</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>发布消息<code>Student.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"student"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学生姓名：{{name}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>h2</span><span>&gt;学生性别：{{sex}}&lt;/</span><span>h2</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>@click</span><span>=</span><span>"sendStudentName"</span><span>&gt;把学生名给School组件&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span>  </span><span>import</span><span> </span><span>pubsub</span><span> </span><span>from</span><span> </span><span>'pubsub-js'</span></div></div><div><div><div>11</div></div><div><span>  </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>name</span><span>:</span><span>'Student'</span><span>,</span></div></div><div><div><div>13</div></div><div><span>    </span><span>data</span><span>() {</span></div></div><div><div><div>14</div></div><div><span>      </span><span>return</span><span> {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>name</span><span>:</span><span>'张三'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>        </span><span>sex</span><span>:</span><span>'男'</span><span>,</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>19</div></div><div><span>    </span><span>mounted</span><span>() {</span></div></div><div><div><div>20</div></div><div><span>      </span><span>// console.log('Student',this.x)</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>22</div></div><div><span>    </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>23</div></div><div><span>      </span><span>sendStudentName</span><span>(){</span></div></div><div><div><div>24</div></div><div><span>        </span><span>// this.$bus.$emit('hello',this.name)</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>pubsub</span><span>.</span><span>publish</span><span>(</span><span>'hello'</span><span>,</span><span>666</span><span>)</span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>slot插槽<a href="#slot插槽"><span>#</span></a></h2><ol>
<li>
<p>插槽（slot）是 vue 为组件的封装者提供的能力。允许开发者在封装组件时，把不确定的、希望由用户指定的部分定义为插槽。可以把插槽认为是组件封装期间，为用户预留的内容的占位符。</p>
</li>
<li>
<p>作用：让父组件可以向子组件指定位置插入html结构，也是一种组件间通信的方式，适用于 <strong>父组件 ===&gt; 子组件</strong> 。</p>
</li>
<li>
<p>分类：默认插槽、具名插槽、作用域插槽</p>
</li>
<li>
<p>注意：<strong>插槽内容是在父组件中编译后, 再传递给子组件的</strong>。</p>
</li>
</ol><section><h3>基本用法<a href="#基本用法"><span>#</span></a></h3><p>vue 官方规定：每一个 <code>slot</code> 插槽，都要有一个 <code>name</code> 名称</p><p>如果省略了 <code>slot</code> 的 <code>name</code> 属性，则有一个默认名称叫做 <code>default</code></p><p>在封装组件时，可以通过 <code>&lt;slot&gt;&lt;/slot&gt;</code> 元素定义插槽，从而为用户预留内容占位符。</p><p>如果在父组件引用子组件中没有结构，则会显示子组件中插槽<code>&lt;slot&gt;&lt;/slot&gt;</code>的内容</p></section><section><h3>默认插槽<a href="#默认插槽"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 父组件中：</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>Category</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;html结构1&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span>&lt;/</span><span>Category</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>//子组件中：</span></div></div><div><div><div>6</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>        </span><span>&lt;!-- 定义插槽 --&gt;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>&lt;</span><span>slot</span><span>&gt;插槽默认内容...&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>具名插槽<a href="#具名插槽"><span>#</span></a></h3><ul>
<li>
<p>子组件：如果在封装组件时需要预留多个插槽节点，则需要为每个 <code>&lt;slot&gt;&lt;/slot&gt;</code> 插槽指定具体的<code>name</code> 名称。这种带有具体名称的插槽叫做“具名插槽”。</p>
</li>
<li>
<p>父组件：在向具名插槽提供内容的时候，我们可以在一个 <code>&lt;template&gt;</code> 元素上使用<code>v-slot</code> 指令，并以<code>v-slot</code> 的参数的形式提供其名称，<code>v-slot</code>也有缩写，即把参数之前的所有内容<code>v-slot:</code> 替换为字符<code>#</code></p>
</li>
</ul><p>注意：没有指定 <code>name</code> 名称的插槽，会有隐含的名称叫做 <code>default</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 父组件中：</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>Category</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>template</span><span> </span><span>slot</span><span>=</span><span>"center"</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span>&gt;html结构1&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>template</span><span> </span><span>v-slot:header</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span>&gt;html结构2&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>&lt;</span><span>template</span><span> </span><span>#footer</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span>&gt;html结构3&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span>&lt;/</span><span>Category</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span>// 子组件中：</span></div></div><div><div><div>16</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span>        </span><span>&lt;!-- 定义插槽 --&gt;</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>&lt;</span><span>slot</span><span> </span><span>name</span><span>=</span><span>"center"</span><span>&gt;插槽默认内容1...&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>&lt;</span><span>slot</span><span> </span><span>name</span><span>=</span><span>"header"</span><span>&gt;插槽默认内容2...&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>&lt;</span><span>slot</span><span> </span><span>name</span><span>=</span><span>"footer"</span><span>&gt;插槽默认内容3...&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>作用域插槽<a href="#作用域插槽"><span>#</span></a></h3><p>理解：<span>数据在组件的自身（子组件），但根据数据生成的结构需要组件的使用者（父组件）来决定。</span>（games数据在Category（子）组件中，但使用数据所遍历出来的结构由App（父）组件决定）</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 父组件中：</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>Category</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>template</span><span> </span><span>scope</span><span>=</span><span>"scopeData"</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span>        </span><span>&lt;!-- 生成的是ul列表 --&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span> </span><span>v-for</span><span>=</span><span>"g in scopeData.games"</span><span> </span><span>:key</span><span>=</span><span>"g"</span><span>&gt;{{g}}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>Category</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>&lt;</span><span>Category</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;</span><span>template</span><span> </span><span>slot-scope</span><span>=</span><span>"scopeData"</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>&lt;!-- 生成的是h4标题 --&gt;</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>&lt;</span><span>h4</span><span> </span><span>v-for</span><span>=</span><span>"g in scopeData.games"</span><span> </span><span>:key</span><span>=</span><span>"g"</span><span>&gt;{{g}}&lt;/</span><span>h4</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span>&lt;/</span><span>Category</span><span>&gt;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>// 子组件中：</span></div></div><div><div><div>19</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span>        </span><span>&lt;!-- 通过数据绑定就可以把子组件的数据传到父组件 --&gt;</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>&lt;</span><span>slot</span><span> </span><span>:games</span><span>=</span><span>"games"</span><span>&gt;&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>27</div></div><div><span>    </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'Category'</span><span>,</span></div></div><div><div><div>29</div></div><div><span>        </span><span>props</span><span>:</span><span> [</span><span>'title'</span><span>],</span></div></div><div><div><div>30</div></div><div><span>        </span><span>//数据在子组件自身</span></div></div><div><div><div>31</div></div><div><span>        </span><span>data</span><span>() {</span></div></div><div><div><div>32</div></div><div><span>            </span><span>return</span><span> {</span></div></div><div><div><div>33</div></div><div><span>                </span><span>games</span><span>:</span><span> [</span><span>'红色警戒'</span><span>, </span><span>'穿越火线'</span><span>, </span><span>'劲舞团'</span><span>, </span><span>'超级玛丽'</span><span>],</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>37</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/an-jian-xiu-shi-fu/</id>
      <title type="text">按键修饰符</title>
      <published>2022-10-06T21:10:42.000Z</published>
      <updated>2022-10-06T21:10:42.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/an-jian-xiu-shi-fu/"/>
      <summary type="text">在绑定了键盘或鼠标的点击事件后,可以通过按下的按键触发要进行的事件,这时需要在后方添加表示按键的修辞符</summary>
      <content type="html"><![CDATA[<p>在绑定了键盘或鼠标的点击事件后,可以通过按下的按键触发要进行的事件,这时需要在后方添加表示按键的修辞符</p>
<section><h2>按键码<a href="#按键码"><span>#</span></a></h2><p>可以通过 <code>keyCode</code> 的值来绑定需要触发的按键</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>input</span><span> </span><span>v-on:keyup.13</span><span>=</span><span>"submit"</span><span> /&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>部分特殊的按键码可以使用别名</strong></p><ul>
<li><code>.enter</code></li>
<li><code>.tab</code></li>
<li><code>.delete</code> (捕获“删除”和“退格”键)</li>
<li><code>.esc</code></li>
<li><code>.space</code></li>
<li><code>.up</code></li>
<li><code>.down</code></li>
<li><code>.left</code></li>
<li><code>.right</code></li>
</ul><p>也可以自定义按键修饰符的别名</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Vue.config.keyCodes.f1 =112</span></div></div><div><div><div>2</div></div><div><span>//使用Vue对象上的全局属性config.keyCodes来定义别名</span></div></div><div><div><div>3</div></div><div><span>//在绑定时就可以使用v-on:keyup.f1直接对f1按键进行操作了</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>系统修饰符<a href="#系统修饰符"><span>#</span></a></h2><p>系统修辞符监听仅在同时按下了绑定键盘或鼠标按钮时才会触发事件</p><section><h3>键盘<a href="#键盘"><span>#</span></a></h3><ul>
<li><code>.ctrl</code></li>
<li><code>.alt</code></li>
<li><code>.shift</code></li>
<li><code>.meta</code></li>
</ul><p><strong>注意</strong>:</p><p>在 Mac 系统键盘上，<code>meta</code> 对应 command 键 (⌘)。在 Windows 系统键盘 <code>meta</code> 对应 Windows 徽标键 (⊞)。</p><p>在 Sun 操作系统键盘上，<code>meta</code> 对应实心宝石键 (◆)。</p><p>在其他特定键盘上，尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品，比如 Knight 键盘、space-cadet 键盘，meta 被标记为“META”。</p><p>在 Symbolics 键盘上，<code>meta</code> 被标记为“META”或者“Meta”。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- Alt + C --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>input</span><span> </span><span>@keyup.alt.67</span><span>=</span><span>"clear"</span><span> /&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!-- Ctrl + Click --&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;</span><span>div</span><span> </span><span>@click.ctrl</span><span>=</span><span>"doSomething"</span><span>&gt;Do something&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>鼠标<a href="#鼠标"><span>#</span></a></h3><ul>
<li><code>.left</code></li>
<li><code>.right</code></li>
<li><code>.middle</code>  鼠标中键</li>
</ul></section></section>
<section><h2>注意事项<a href="#注意事项"><span>#</span></a></h2><p>注&lt;修饰键与常规按键不同&gt;，在和 <code>keyup</code> 事件一起用时，事件触发时修饰键必须处于按下状态。</p><p>换句话说，只有在按住 <code>ctrl</code> 的情况下释放其它按键，才能触发 <code>keyup.ctrl</code>。而单单释放 <code>ctrl</code> 也不会触发事件。如果只想要单独触发，则需要使用 <code>keycode</code> 编码或<code>.exact</code>修饰符</p><p><code>.exact</code></p><p><code>.exact</code> 修饰符允许用户控制由精确的系统修饰符组合触发的事件</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 即使 Alt 或 Shift 被一同按下时也会触发 --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>button</span><span> </span><span>@click.ctrl</span><span>=</span><span>"onClick"</span><span>&gt;A&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!-- 有且只有 Ctrl 被按下的时候才触发 --&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;</span><span>button</span><span> </span><span>@click.ctrl.exact</span><span>=</span><span>"onCtrlClick"</span><span>&gt;A&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;!-- 没有任何系统修饰符被按下的时候才触发 --&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;</span><span>button</span><span> </span><span>@click.exact</span><span>=</span><span>"onClick"</span><span>&gt;A&lt;/</span><span>button</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-zu-jian/</id>
      <title type="text">Vue 组件</title>
      <published>2022-10-06T07:30:42.000Z</published>
      <updated>2022-10-06T07:30:42.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-zu-jian/"/>
      <summary type="text">组件的出现是为了拆分 Vue 实例的代码量，能够让我们以不同的组件来划分不同的功能模板，将来需要什么样的功能，只需要调用对应的组件就可以了 组件化与模块化的区别 模块化是从代码逻辑的角度进行划分的;方便代码分层开发，保证每个功能模块的职能单一 组件化是从 UI 界面的角度进行划分的，前端的组件化是为…</summary>
      <content type="html"><![CDATA[<section><h2>组件的定义<a href="#组件的定义"><span>#</span></a></h2><p><strong>组件的出现是为了拆分 Vue 实例的代码量，能够让我们以不同的组件来划分不同的功能模板，将来需要什么样的功能，只需要调用对应的组件就可以了</strong></p><p><strong>组件化与模块化的区别</strong></p><ul>
<li>模块化是从代码逻辑的角度进行划分的;方便代码分层开发，保证每个功能模块的职能单一</li>
<li>组件化是从 UI 界面的角度进行划分的，前端的组件化是为了方便 UI 组件的重用</li>
</ul></section>
<section><h2>组件基本使用<a href="#组件基本使用"><span>#</span></a></h2><p><strong>组件的注意事项:</strong></p><ul>
<li>**每个组件必须只有一个根元素，**可以将模板的内容包裹在一个父元素内来解决这个问题</li>
<li>组件中可以有自己的 <code>data</code> 数据，使用方式同 Vue 实例中一样，<strong>但是组件的 <code>data</code> 只能是一个方法，而且必须要返回一个包含了数据的对象</strong>，因为需要每个组件之间相互独立，如果不是一个函数，那么每个组件就会影响其他的组件 <strong>如果要想同时影响其他数据，可以创建一个外部的镀锡，每次 <code>data</code> 返回这个对象</strong></li>
</ul><section><h3>组件名<a href="#组件名"><span>#</span></a></h3><p><strong>在创建一个组件时，我们始终需要给该组件一个名字用作标识</strong>，组件名的有多种写法</p><ul>
<li>
<p><strong>短横线命名法</strong>，使用短横线命名法来命名组件在使用的时候也是直接将引用的组件名作为标签名来使用</p>
</li>
<li>
<p>**驼峰命名法，**使用驼峰命名法命名的组件在引用这个组件时可以通过两种命名的方式来引用</p>
<p>**注意:**当用作标签时只能使用短横线命名法，所以最好都使用短横线命名法来命名</p>
</li>
</ul></section><section><h3>创建组件<a href="#创建组件"><span>#</span></a></h3><ul>
<li>方式1：先创建一个组件构造器，然后由组件构造器创建组件，如:<code>var myCom =Vue.extend(option)</code></li>
<li>方式2：使用<code>Vue.component()</code>直接创建组件。<code>Vue.component('componentName',option) </code>， <code>option</code>相当于创建vue对象传入的配置对象</li>
<li>方式3：把组件的 <code>name</code> 属性作为注册后组件的名称</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>//方式1 创建hello组件</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const hello = Vue.extend({</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>template:`</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;div&gt;</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>&lt;h2&gt;你好啊！{{name}}&lt;/h2&gt;</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>`,</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>data(){</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>return {</span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>name:'Tom'</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>// 方式2</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>Vue.component('hello', {</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>template:`</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>&lt;div&gt;</span></div></div><div><div><div>19</div></div><div><span><span>          </span></span><span>&lt;h2&gt;你好啊！{{name}}&lt;/h2&gt;</span></div></div><div><div><div>20</div></div><div><span><span>          </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>`,</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>data(){</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>return {</span></div></div><div><div><div>24</div></div><div><span><span>                </span></span><span>name:'Tom'</span></div></div><div><div><div>25</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>//方式3</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>&lt;template&gt;</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>&lt;div&gt;</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>&lt;h2&gt;你好啊！{{name}}&lt;/h2&gt;</span></div></div><div><div><div>33</div></div><div><span><span>       </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>&lt;/template&gt;</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>&lt;script&gt;</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>export default {</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>name: 'hello',</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>data(){</span></div></div><div><div><div>40</div></div><div><span><span>            </span></span><span>return {</span></div></div><div><div><div>41</div></div><div><span><span>                </span></span><span>name:'Tom'</span></div></div><div><div><div>42</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>注册组件<a href="#注册组件"><span>#</span></a></h3><section><h4>全局组件注册<a href="#全局组件注册"><span>#</span></a></h4><p>在<code>&lt;script&gt;</code>标签中直接使用 <code>Vue.component(componentName, component)</code> 方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;script&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>//全局注册组件 hello</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Vue.component('school', school)</span></div></div><div><div><div>4</div></div><div><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>局部组件注册<a href="#局部组件注册"><span>#</span></a></h4><p>在需要使用的页面中，创建的<code>vue</code> 实例中，<code>component</code>项中注册</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;script&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>//创建vm</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>new Vue({</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>el: '#root',</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>data: {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>msg:'你好啊！'</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>//注册组件（局部注册）</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>components: {</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>hello: hello,</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>// ES6简写形式</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>// hello,</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>15</div></div><div><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>区别<a href="#区别"><span>#</span></a></h4><ul>
<li>被全局注册的组件，可以在全局任何一个组件内使用</li>
<li>被局部注册的组件，只能在当前注册的范围内使用</li>
</ul><p><strong>应用场景</strong></p><ul>
<li>如果某些组件在开发期间的使用频率很高，推荐进行全局注册</li>
<li>如果某些组件只在特定的情况下会被用到，推荐进行局部注册</li>
</ul></section></section><section><h3>使用组件<a href="#使用组件"><span>#</span></a></h3><p>在页面注册组件后，使用 <code>&lt;组件名/&gt;</code> 或 <code>&lt;组件名&gt;&lt;组件名/&gt;</code> 的方式</p><section><h4>关于组件名<a href="#关于组件名"><span>#</span></a></h4><p>一个单词组成：</p><ul>
<li>第一种写法(首字母小写)：<code>school</code></li>
<li>第二种写法(首字母大写)：<code>School</code></li>
</ul><p>多个单词组成：</p><ul>
<li>第一种写法(kebab-case命名)：<code>my-school</code></li>
<li>第二种写法(CamelCase命名)：<code>MySchool</code> (需要Vue脚手架支持)</li>
</ul><blockquote><p>备注：</p><p>(1).组件名尽可能回避HTML中已有的元素名称，例如：h2、H2都不行。</p><p>(2).可以使用<code>name</code>配置项指定组件在开发者工具中呈现的名字。</p></blockquote></section><section><h4>关于组件标签<a href="#关于组件标签"><span>#</span></a></h4><p>第一种写法：<code>&lt;school&gt;&lt;/school&gt;</code></p><p>第二种写法：<code>&lt;school/&gt;</code></p><blockquote><p>备注：不用使用脚手架时，<code>&lt;school/&gt;</code>会导致后续组件不能渲染。</p></blockquote></section></section></section>
<section><h2>VueComponent<a href="#vuecomponent"><span>#</span></a></h2><ul>
<li><code>hello</code>组件本质是一个名为<code>VueComponent</code>的构造函数，且不是程序员定义的，是<code>Vue.extend()</code>生成的。</li>
<li>我们只需要写<code>&lt;school/&gt;</code>或<code>&lt;school&gt;&lt;/school&gt;</code>，<code>Vue</code>解析时会帮我们创建<code>hello</code>组件的实例对象，即<code>Vue</code>帮我们执行的：<code>new VueComponent(options)</code>。</li>
<li>特别注意：<strong>每次调用<code>Vue.extend</code>，返回的都是一个全新的<code>VueComponent</code></strong>！(这个<code>VueComponent</code>可不是实例对象)</li>
<li>关于<code>this</code>指向：
<ul>
<li>组件配置中：<code>data</code>函数、<code>methods</code>中的函数、<code>watch</code>中的函数、<code>computed</code>中的函数 它们的<code>this</code>均是【<code>VueComponent</code>实例对象】。</li>
<li><code>new Vue(options)</code>配置中：<code>data</code>函数、<code>methods</code>中的函数、<code>watch</code>中的函数、<code>computed</code>中的函数 它们的<code>this</code>均是【<code>Vue</code>实例对象】。</li>
</ul>
</li>
<li><code>VueComponent</code>的实例对象，以后简称<code>vc</code>（也可称之为：组件实例对象）。<code>Vue</code>的实例对象，以后简称<code>vm</code>。</li>
</ul><section><h3>一个重要的内置关系<a href="#一个重要的内置关系"><span>#</span></a></h3><ul>
<li>一个重要的内置关系：<code>VueComponent.prototype.__proto__ === Vue.prototype</code></li>
<li>为什么要有这个关系：让组件实例对象（vc）可以访问到 Vue原型上的属性、方法。</li>
</ul><p></p><figure><img src="/images/vue/vue_%E7%BB%84%E4%BB%B6_%E4%B8%80%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E5%86%85%E7%BD%AE%E5%85%B3%E7%B3%BB.png" alt="vue_组件_一个重要的内置关系" /><figcaption>vue_组件_一个重要的内置关系</figcaption></figure><p></p></section></section>
<section><h2>Props<a href="#props"><span>#</span></a></h2><ol>
<li>
<p>功能：让组件接收外部传过来的数据</p>
</li>
<li>
<p>传递数据：<code>&lt;Demo name="xxx"/&gt;</code></p>
</li>
<li>
<p>接收数据：</p>
<ol>
<li>
<p>第一种方式（只接收）：<code>props:['name'] </code></p>
</li>
<li>
<p>第二种方式（限制类型）：<code>props:{name:String}</code></p>
</li>
<li>
<p>第三种方式（限制类型、限制必要性、指定默认值）：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>props</span><span>:{</span></span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>name</span><span>:{</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>type</span><span>:</span><span>String</span><span>, </span><span>//类型</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>required</span><span>:</span><span>true</span><span>, </span><span>//必要性</span></div></div><div><div><div>5</div></div><div><span>        </span><span>default</span><span>:</span><span>'老王'</span><span> </span><span>//默认值</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol>
<blockquote><p>备注：<code>props</code>是只读的，Vue底层会监测你对<code>props</code>的修改，如果进行了修改，就会发出警告，若业务需求确实需要修改，那么请复制<code>props</code>的内容到<code>data</code>中一份，然后去修改<code>data</code>中的数据。</p></blockquote>
</li>
</ol><p>子组件是无法直接使用父组件的 <code>data</code> 中的数据的，需要通过 <code>props</code> 属性才能够使用父组件传入过来的值</p><section><h3>向子组件传入数据<a href="#向子组件传入数据"><span>#</span></a></h3><p>在父组件的引用中填入属性名与对应的属性值,然后在子组件的 <code>props</code> 值填入与引用子组件的属性名一致的属性名，就可以在子组件中引用父组件的数据了，就如访问 <code>data</code> 中的值一样</p><p>父组件 <code>App.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"app"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>// 向子组件传入 {name:'haha', age:this.age}</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;</span><span>School</span><span> </span><span>name</span><span>=</span><span>"haha"</span><span> :</span><span>age</span><span>=</span><span>"</span><span>this</span><span>.</span><span>age</span><span>"</span><span>&gt;&lt;/</span><span>School</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>import</span><span> </span><span>School</span><span> </span><span>from</span><span> </span><span>'./components/School.vue'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>name</span><span>:</span><span> </span><span>'App'</span><span>,</span></div></div><div><div><div>13</div></div><div><span>        </span><span>data</span><span>() {</span></div></div><div><div><div>14</div></div><div><span>            </span><span>return</span><span> {</span></div></div><div><div><div>15</div></div><div><span>                </span><span>age</span><span>:</span><span> </span><span>360</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span>        </span><span>components</span><span>:</span><span> {</span></div></div><div><div><div>19</div></div><div><span>            </span><span>School</span><span>,</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>子组件 <code>School.vue</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;template&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;div class="demo"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;h2&gt;学校名称：{{ name }}&lt;/h2&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;h2&gt;学校年龄：{{ age }}&lt;/h2&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;h2&gt;学校地址：{{ address }}&lt;/h2&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;/template&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&lt;script&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>export default {</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>name: 'School',</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>// 最简单的写法：props: ['name', 'age']</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>props: {</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>name: {</span></div></div><div><div><div>15</div></div><div><span><span>                </span></span><span>type: String,</span></div></div><div><div><div>16</div></div><div><span><span>                </span></span><span>required: true, // 必须要传的</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>age: {</span></div></div><div><div><div>19</div></div><div><span><span>                </span></span><span>type: Number,</span></div></div><div><div><div>20</div></div><div><span><span>                </span></span><span>required: true,</span></div></div><div><div><div>21</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>data() {</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>return {</span></div></div><div><div><div>25</div></div><div><span><span>                </span></span><span>address: '北京昌平',</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>注意:</strong></p><ul>
<li><strong>一个组件默认可以拥有任意数量的 <code>prop</code></strong>，任何值都可以传递给任何 <code>prop</code>，但是为了方便可以传入一个对象,调用对象的属性来使用传入的数据</li>
<li>组件中的所有 <code>props</code> 中的数据都是通过父组件传递给子组件的，**不要试图去修改 <code>props</code> 中传入的数据,**一般都是只读的，如果修改 Vue 会抛出报错信息</li>
<li><strong>可以通过 <code>v-bind</code> 动态传递给 <code>prop</code> 一个值</strong></li>
<li>子组件的 <code>data</code> 数据并不是通过父组件传递过来的,而是子组件自身私有的，比如子组件通过 <code>Ajax</code> 请求回来的数据都可以放在 <code>data</code> 身上，子组件的 <code>data</code> 上的数据都是可读可写的</li>
</ul></section><section><h3>Prop 的大小写<a href="#prop-的大小写"><span>#</span></a></h3><p>HTML 中的特性名是大小写不敏感的，所以浏览器会把所有大写字符解释为小写字符。这意味着当使用 DOM 中的模板时，<strong>驼峰命名法的 <code>prop</code> 名需要使用其等价的短横线分隔命名</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>post-title</span><span>=</span><span>"hello!"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>Vue</span><span>.</span><span>component</span><span>(</span><span>'blog-post'</span><span>, {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>props</span><span>:</span><span> [</span><span>'postTitle'</span><span>],</span></div></div><div><div><div>6</div></div><div><span>        </span><span>template</span><span>:</span><span> </span><span>'&lt;h3&gt;{{ postTitle }}&lt;/h3&gt;'</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>8</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Prop 类型<a href="#prop-类型"><span>#</span></a></h3><p>如果希望传入的 <code>prop</code> 都有指令的值类型,可以用对象的方式来，可以以对象形式列出 <code>prop</code>，这些属性的名称和值分别是 <code>prop</code> 各自的名称和类型。除此之外，还可以用于配置其他高级选项</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//数组形式</span></div></div><div><div><div>2</div></div><div><span>props: ['title', 'likes', 'isPublished', 'commentIds', 'author']</span></div></div><div><div><div>3</div></div><div><span>//对象形式</span></div></div><div><div><div>4</div></div><div><span>props: {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>title: String,</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>likes: Number,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>isPublished: Boolean,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>commentIds: Array,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>author: Object,</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>// 简单语法</span></div></div><div><div><div>12</div></div><div><span>Vue.component('props-demo-simple', {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>props: ['size', 'myMessage'],</span></div></div><div><div><div>14</div></div><div><span>})</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 对象语法，提供校验</span></div></div><div><div><div>17</div></div><div><span>Vue.component('props-demo-advanced', {</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>props: {</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>// 检测类型</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>height: Number,</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>// 检测类型 + 其他验证</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>age: {</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>type: Number,</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>default: 0, //设置默认值</span></div></div><div><div><div>25</div></div><div><span><span>            </span></span><span>required: true, //是否必须</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>validator: function (value) {</span></div></div><div><div><div>27</div></div><div><span><span>                </span></span><span>//对值进行验证</span></div></div><div><div><div>28</div></div><div><span><span>                </span></span><span>return value &gt;= 0</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>32</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>动态传递 Prop<a href="#动态传递-prop"><span>#</span></a></h3><p>可以通过 <code>v-bind</code> 的方式动态传递一个变量值给一个 <code>prop</code>，通过 <code>name="xxx"</code>的方式，会将 <code>xxx</code> 解析为字符串，因此应用 <code>v-bind</code>方式传入一个 js 表达式</p><ul>
<li>传入一个数字</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 即便 `42` 是静态的，我们仍然需要 `v-bind` 来告诉 Vue --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;!-- 这是一个 JavaScript 表达式而不是一个字符串。--&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:likes</span><span>=</span><span>"42"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>&lt;!-- 用一个变量进行动态赋值。--&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:likes</span><span>=</span><span>"post.likes"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>传入一个布尔值</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 包含该 prop 没有值的情况在内，都意味着 `true`。--&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>is-published</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!-- 即便 `false` 是静态的，我们仍然需要 `v-bind` 来告诉 Vue --&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;!-- 这是一个 JavaScript 表达式而不是一个字符串。--&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:is-published</span><span>=</span><span>"false"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>&lt;!-- 用一个变量进行动态赋值。--&gt;</span></div></div><div><div><div>9</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:is-published</span><span>=</span><span>"post.isPublished"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>传入一个数组</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 即便数组是静态的，我们仍然需要 `v-bind` 来告诉 Vue --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;!-- 这是一个 JavaScript 表达式而不是一个字符串。--&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:comment-ids</span><span>=</span><span>"[234, 266, 273]"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>&lt;!-- 用一个变量进行动态赋值。--&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:comment-ids</span><span>=</span><span>"post.commentIds"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>传入一个对象</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 即便对象是静态的，我们仍然需要 `v-bind` 来告诉 Vue --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;!-- 这是一个 JavaScript 表达式而不是一个字符串。--&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;</span><span>blog-post</span></div></div><div><div><div>4</div></div><div><span>    </span><span>v-bind:author</span><span>=</span><span>"{</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>name: 'Veronica',</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>company: 'Veridian Dynamics'</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}"</span></div></div><div><div><div>8</div></div><div><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&lt;!-- 用一个变量进行动态赋值。--&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind:author</span><span>=</span><span>"post.author"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>传入一个对象的所有属性</li>
</ul><p>如果想要将一个对象的所有属性都作为 prop 传入，你可以使用不带参数的 <code>v-bind</code>(取代 <code>v-bind:prop-name</code>)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>post</span><span>: {</span></span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>id</span><span>: </span><span>1</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>title</span><span>: </span><span>'My Journey with Vue'</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind</span><span>=</span><span>"post"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;!--</span><span>与下面的方法等价</span><span>--&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;</span><span>blog-post</span><span> </span><span>v-bind</span><span>:</span><span>id</span><span>=</span><span>"post.id"</span><span>  </span><span>v-bind</span><span>:</span><span>title</span><span>=</span><span>"post.title"</span><span>&gt;&lt;/</span><span>blog-post</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>$ref 属性<a href="#ref-属性"><span>#</span></a></h2><ul>
<li>被用来给元素或子组件注册引用信息（<code>id</code>的替代者），通过这个引用的 ID,父组件可以直接通过<code>this.$refs</code>访问这个组件或子元素</li>
<li>应用在<code>html</code>标签上获取的是真实DOM元素，应用在组件标签上是组件实例对象（vc）</li>
<li>使用方式：
<ul>
<li>打标识：<code>&lt;h1 ref="xxx"&gt;&lt;/h1&gt;</code>或 <code>&lt;School ref="xxx"&gt;&lt;/School&gt;</code></li>
<li>获取：<code>this.$refs.xxx</code></li>
</ul>
</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"app"</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;</span><span>input</span><span> </span><span>type</span><span>=</span><span>"button"</span><span> </span><span>value</span><span>=</span><span>"获取元素内容"</span><span> </span><span>@click</span><span>=</span><span>"getElement"</span><span> /&gt;</span></div></div><div><div><div>4</div></div><div><span>        </span><span>&lt;!-- 使用 ref 获取元素 --&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;</span><span>h1</span><span> </span><span>ref</span><span>=</span><span>"myh1"</span><span>&gt;这是一个大大的H1&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>&lt;</span><span>hr</span><span> /&gt;</span></div></div><div><div><div>8</div></div><div><span>        </span><span>&lt;!-- 使用 ref 获取子组件 --&gt;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>&lt;</span><span>my-com</span><span> </span><span>ref</span><span>=</span><span>"mycom"</span><span>&gt;&lt;/</span><span>my-com</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>Vue</span><span>.</span><span>component</span><span>(</span><span>'my-com'</span><span>, {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>template</span><span>:</span><span> </span><span>'&lt;h5&gt;这是一个子组件&lt;/h5&gt;'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>        </span><span>data</span><span>() {</span></div></div><div><div><div>17</div></div><div><span>            </span><span>return</span><span> {</span></div></div><div><div><div>18</div></div><div><span>                </span><span>name</span><span>:</span><span> </span><span>'子组件'</span><span>,</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>// 创建 Vue 实例，得到 ViewModel</span></div></div><div><div><div>24</div></div><div><span>    </span><span>var</span><span><span> </span><span>vm</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>25</div></div><div><span>        </span><span>el</span><span>:</span><span> </span><span>'#app'</span><span>,</span></div></div><div><div><div>26</div></div><div><span>        </span><span>data</span><span>:</span><span> {},</span></div></div><div><div><div>27</div></div><div><span>        </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>28</div></div><div><span>            </span><span>getElement</span><span>() {</span></div></div><div><div><div>29</div></div><div><span>                </span><span>// 通过 this.$refs 来获取元素</span></div></div><div><div><div>30</div></div><div><span><span>                </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>$refs</span><span>.</span><span>myh1</span><span>.</span><span>innerText</span><span>) </span><span>// 这是一个大大的H1</span></div></div><div><div><div>31</div></div><div><span>                </span><span>// 通过 this.$refs 来获取组件</span></div></div><div><div><div>32</div></div><div><span><span>                </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>$refs</span><span>.</span><span>mycom</span><span>.</span><span>name</span><span>) </span><span>// 子组件</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>36</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/guo-du-yu-dong-hua/</id>
      <title type="text">过渡与动画</title>
      <published>2022-10-06T05:35:27.000Z</published>
      <updated>2022-10-06T05:35:27.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/guo-du-yu-dong-hua/"/>
      <summary type="text">Vue 中可以通过 transition 组件来实现过渡效果 Vue 在插入,更新或者移除 DOM 时,提供多种不同方式的应用过渡效果 在 CSS 过渡和动画中自动应用 class 可以配合使用第三方 CSS 动画库，如 Animate.css 在过渡钩子函数中使用 JavaScript 直接操作…</summary>
      <content type="html"><![CDATA[<p><strong>Vue 中可以通过<code>transition</code>组件来实现过渡效果 Vue 在插入,更新或者移除 DOM 时,提供多种不同方式的应用过渡效果</strong></p>
<ul>
<li>在 CSS 过渡和动画中自动应用 class</li>
<li>可以配合使用第三方 CSS 动画库，如 Animate.css</li>
<li>在过渡钩子函数中使用 JavaScript 直接操作 DOM</li>
<li>可以配合使用第三方 JavaScript 动画库，如 Velocity.js</li>
</ul>
<section><h2>过渡的类名<a href="#过渡的类名"><span>#</span></a></h2><section><h3>自身实现的类名<a href="#自身实现的类名"><span>#</span></a></h3><p><strong>这些类名是 transition 组件中自定好的,在使用的时候只需要在 CSS 中写入特定的类名和样式就可以了</strong></p><ul>
<li><code>v-enter</code>：定义进入过渡的开始状态。在元素被插入之前生效，此时元素还没有进入过渡，在元素被插入之后的下一帧移除</li>
<li><code>v-enter-active</code>：定义进入过渡生效时的状态。在整个进入过渡的阶段中应用，在元素被插入之前生效，在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间，延迟和曲线函数</li>
<li><code>v-enter-to</code>: 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 <code>v-enter</code> 被移除)，在过渡/动画完成之后移除</li>
<li><code>v-leave</code>: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效，下一帧被移除</li>
<li><code>v-leave-active</code>：定义离开过渡生效时的状态。在整个离开过渡的阶段中应用，在离开过渡被触发时立刻生效，在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间，延迟和曲线函数</li>
<li><code>v-leave-to</code>&lt;定义离开过渡的结束状态&gt;。在离开过渡被触发之后下一帧生效 (与此同时 <code>v-leave</code> 被删除)，在过渡/动画完成之后移除</li>
</ul><p><strong>注:</strong></p><ul>
<li><strong>一般都是使用 <code>v-enter</code>,<code>v-leave-to</code> 与 <code>v-enter-active</code> 和 <code>v-leave-active</code> 的组合</strong></li>
<li>对于这些在过渡中切换的类名来说,如果使用一个没有名字的 <code>&lt;transition&gt;</code>,则 <code>v-</code> 是这些类名的默认前缀,如果使用了 <code>&lt;transition name="my-transition"&gt;</code>,那么 <code>v-enter</code> 会替换为 <code>my-transition-enter</code>,通过这个可以绑定:<code>name</code> 实现动态过渡的效果</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;style&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// 动画进入，离开的样式</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>.fade-enter-active,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>.fade-leave-active {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>transition: opacity 0.5s;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// 动画进入的起点，动画离开的终点</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>.fade-enter,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>.fade-leave-to {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>opacity: 0;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>&lt;/style&gt;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>&lt;div id="demo"&gt;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>&lt;button v-on:click="show = !show"&gt;Toggle&lt;/button&gt;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>&lt;transition name="fade"&gt;</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>&lt;p v-if="show"&gt;hello&lt;/p&gt;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>&lt;/transition&gt;</span></div></div><div><div><div>19</div></div><div><span>&lt;/div&gt;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>&lt;script&gt;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>new Vue({</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>el: '#demo',</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>data: {</span></div></div><div><div><div>25</div></div><div><span><span>            </span></span><span>show: true,</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>28</div></div><div><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>动画效果同过渡效果，只是在动画中 <code>v-enter</code> 这个类名在插入 DOM 中后不会立刻被删除，而是在<code>animationend</code>事件触发时才会被删除</p></section><section><h3>自定义过渡的类名<a href="#自定义过渡的类名"><span>#</span></a></h3><p><strong>可以通过引入第三方库实现过渡效果,这时需要使用自定义过渡的类名来加第三方库的过渡效果使用</strong></p><p>可以通过以下特性来自定义过渡类名,这些类名分别也对应着上方的执行时期,它们的优先级高于普通的类名</p><ul>
<li><code>enter-class</code></li>
<li><code>enter-active-class</code></li>
<li><code>enter-to-class</code></li>
<li><code>leave-class</code></li>
<li><code>leave-active-class</code></li>
<li><code>leave-to-class</code></li>
</ul><p><strong>在这些自定义的属性中可以写入要引入的第三方库的 CSS 样式就能实现效果</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;link</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1"</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>rel="stylesheet"</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>type="text/css"</span></div></div><div><div><div>5</div></div><div><span>/&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;div id="example"&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;button @click="show = !show"&gt;Toggle render&lt;/button&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;transition</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>name="custom-classes-transition"</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>enter-active-class="animated tada"</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>leave-active-class="animated bounceOutRight"</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>&lt;p v-if="show"&gt;hello&lt;/p&gt;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>&lt;/transition&gt;</span></div></div><div><div><div>16</div></div><div><span>&lt;/div&gt;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>&lt;script&gt;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>new Vue({</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>el: '#example',</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>data: {</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>show: true,</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>25</div></div><div><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>实例<a href="#实例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!DOCTYPE html&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;html lang="en"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;head&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;meta charset="UTF-8" /&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;title&gt;过渡与动画&lt;/title&gt;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>&lt;style&gt;</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>.fade1-enter,</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>.fade1-leave-to {</span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>opacity: 0;</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>.fade1-enter-active,</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>.fade1-leave-active {</span></div></div><div><div><div>13</div></div><div><span><span>                </span></span><span>transition: opacity 1s;</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>.fade2-enter {</span></div></div><div><div><div>16</div></div><div><span><span>                </span></span><span>opacity: 0;</span></div></div><div><div><div>17</div></div><div><span><span>                </span></span><span>transform: translateX(-20px);</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>.fade2-leave-to {</span></div></div><div><div><div>20</div></div><div><span><span>                </span></span><span>opacity: 0;</span></div></div><div><div><div>21</div></div><div><span><span>                </span></span><span>transform: translateX(20px);</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>.fade2-enter-active,</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>.fade2-leave-active {</span></div></div><div><div><div>25</div></div><div><span><span>                </span></span><span>transition: all 3s;</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>&lt;/style&gt;</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>&lt;/head&gt;</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>&lt;body&gt;</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>&lt;!--</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>1. vue动画的理解</span></div></div><div><div><div>32</div></div><div><span><span>            </span></span><span>操作css的transition或animation</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>vue会给目标元素添加/移除特定的class</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>2. 基本过渡动画的编码</span></div></div><div><div><div>35</div></div><div><span><span>            </span></span><span>1). 在目标元素外包裹&lt;transition name="xxx"&gt;</span></div></div><div><div><div>36</div></div><div><span><span>            </span></span><span>2). 定义class样式</span></div></div><div><div><div>37</div></div><div><span><span>                </span></span><span>1&gt;. 指定过渡样式: transition</span></div></div><div><div><div>38</div></div><div><span><span>                </span></span><span>2&gt;. 指定隐藏时的样式: opacity/其它</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>3. 过渡的类名</span></div></div><div><div><div>40</div></div><div><span><span>            </span></span><span>xxx-enter-active: 指定显示的transition</span></div></div><div><div><div>41</div></div><div><span><span>            </span></span><span>xxx-leave-active: 指定隐藏的transition</span></div></div><div><div><div>42</div></div><div><span><span>            </span></span><span>xxx-enter: 指定隐藏时的样式</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>--&gt;</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>&lt;div id="app1"&gt;</span></div></div><div><div><div>45</div></div><div><span><span>            </span></span><span>&lt;button @click="isShow = !isShow"&gt;Destroy&lt;/button&gt;</span></div></div><div><div><div>46</div></div><div><span><span>            </span></span><span>&lt;transition name="fade1"&gt;</span></div></div><div><div><div>47</div></div><div><span><span>                </span></span><span>&lt;p v-if="isShow"&gt;Hello&lt;/p&gt;</span></div></div><div><div><div>48</div></div><div><span><span>            </span></span><span>&lt;/transition&gt;</span></div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>&lt;div id="app2"&gt;</span></div></div><div><div><div>51</div></div><div><span><span>            </span></span><span>&lt;button @click="isShow = !isShow"&gt;Destroy&lt;/button&gt;</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>&lt;transition name="fade2"&gt;</span></div></div><div><div><div>53</div></div><div><span><span>                </span></span><span>&lt;p v-if="isShow"&gt;Hello&lt;/p&gt;</span></div></div><div><div><div>54</div></div><div><span><span>            </span></span><span>&lt;/transition&gt;</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>56</div></div><div><span><span>        </span></span><span>&lt;script type="text/javascript" src="./vue.js"&gt;&lt;/script&gt;</span></div></div><div><div><div>57</div></div><div><span><span>        </span></span><span>&lt;script type="text/javascript"&gt;</span></div></div><div><div><div>58</div></div><div><span><span>            </span></span><span>new Vue({</span></div></div><div><div><div>59</div></div><div><span><span>                </span></span><span>el: '#app1',</span></div></div><div><div><div>60</div></div><div><span><span>                </span></span><span>data() {</span></div></div><div><div><div>61</div></div><div><span><span>                    </span></span><span>return {</span></div></div><div><div><div>62</div></div><div><span><span>                        </span></span><span>isShow: 'false',</span></div></div><div><div><div>63</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>64</div></div><div><span><span>                </span></span><span>},</span></div></div><div><div><div>65</div></div><div><span><span>            </span></span><span>})</span></div></div><div><div><div>66</div></div><div><span><span>            </span></span><span>new Vue({</span></div></div><div><div><div>67</div></div><div><span><span>                </span></span><span>el: '#app2',</span></div></div><div><div><div>68</div></div><div><span><span>                </span></span><span>data() {</span></div></div><div><div><div>69</div></div><div><span><span>                    </span></span><span>return {</span></div></div><div><div><div>70</div></div><div><span><span>                        </span></span><span>isShow: 'false',</span></div></div><div><div><div>71</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>72</div></div><div><span><span>                </span></span><span>},</span></div></div><div><div><div>73</div></div><div><span><span>            </span></span><span>})</span></div></div><div><div><div>74</div></div><div><span><span>        </span></span><span>&lt;/script&gt;</span></div></div><div><div><div>75</div></div><div><span><span>    </span></span><span>&lt;/body&gt;</span></div></div><div><div><div>76</div></div><div><span>&lt;/html&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>初始渲染过渡<a href="#初始渲染过渡"><span>#</span></a></h2><p><strong>为<code>transition</code>组件添加一个简单的属性 appear 就能够实现在刚开始渲染的时候就使用一次<code>enter-active-class</code>来实现开始渲染列表的过渡效果</strong></p><p><strong>初始渲染时也可以使用自定义的 CSS 类名进行初始渲染</strong></p><ul>
<li><code>appear-class</code></li>
<li><code>appear-active-class</code></li>
<li><code>appear-to-class</code></li>
</ul><p><strong>注意</strong>&lt;使用自定义类名进行渲染时也需要添加&gt; appear 属性</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>transition</span></div></div><div><div><div>2</div></div><div><span>    </span><span>appear</span></div></div><div><div><div>3</div></div><div><span>    </span><span>appear-class</span><span>=</span><span>"custom-appear-class"</span></div></div><div><div><div>4</div></div><div><span>    </span><span>appear-to-class</span><span>=</span><span>"custom-appear-to-class"</span></div></div><div><div><div>5</div></div><div><span>    </span><span>appear-active-class</span><span>=</span><span>"custom-appear-active-class"</span></div></div><div><div><div>6</div></div><div><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>&lt;!-- ... --&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;/</span><span>transition</span><span>&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&lt;!--当然也可以使用JS的钩子函数--&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;</span><span>transition</span></div></div><div><div><div>12</div></div><div><span>    </span><span>appear</span></div></div><div><div><div>13</div></div><div><span>    </span><span>v-on:before-appear</span><span>=</span><span>"customBeforeAppearHook"</span></div></div><div><div><div>14</div></div><div><span>    </span><span>v-on:appear</span><span>=</span><span>"customAppearHook"</span></div></div><div><div><div>15</div></div><div><span>    </span><span>v-on:after-appear</span><span>=</span><span>"customAfterAppearHook"</span></div></div><div><div><div>16</div></div><div><span>    </span><span>v-on:appear-cancelled</span><span>=</span><span>"customAppearCancelledHook"</span></div></div><div><div><div>17</div></div><div><span>&gt;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>&lt;!-- ... --&gt;</span></div></div><div><div><div>19</div></div><div><span>&lt;/</span><span>transition</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>显性定义过渡时间<a href="#显性定义过渡时间"><span>#</span></a></h2><p><strong>默认情况下，Vue 会等待其在过渡效果的根元素的第一个 <code>transitionend</code> 或 <code>animationend</code> 事件</strong>。然而也可以不这样设定——比如，我们可以拥有一个精心编排的一系列过渡效果，其中一些嵌套的内部元素相比于过渡效果的根元素有延迟的或更长的过渡效果。</p><p>在这种情况下，<strong>可以用 <code>&lt;transition&gt;</code> 组件上的绑定的 <code>duration</code> 属性定制一个显性的过渡持续时间 (以毫秒为单位)</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>transition</span><span> </span><span>:duration</span><span>=</span><span>"1000"</span><span>&gt;...&lt;/</span><span>transition</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>可以传入一个对象分别对移入和移除的时间进行设置</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>transition</span><span> </span><span>:duration</span><span>=</span><span>"{ enter: 500, leave: 800 }"</span><span>&gt;...&lt;/</span><span>transition</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>JS 钩子<a href="#js-钩子"><span>#</span></a></h2><p><strong>除了通过 CSS 实现过渡效果，还可以用 JS 中的钩子函数实现过渡效果</strong>，这些钩子可以结合 CSS 使用，也可以全部单独使用</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;</span><span>transition</span></div></div><div><div><div>2</div></div><div><span>        </span><span>v-on:before-enter</span><span>=</span><span>"beforeEnter"</span></div></div><div><div><div>3</div></div><div><span>        </span><span>v-on:enter</span><span>=</span><span>"enter"</span></div></div><div><div><div>4</div></div><div><span>        </span><span>v-on:after-enter</span><span>=</span><span>"afterEnter"</span></div></div><div><div><div>5</div></div><div><span>        </span><span>v-on:enter-cancelled</span><span>=</span><span>"enterCancelled"</span></div></div><div><div><div>6</div></div><div><span>        </span><span>v-on:before-leave</span><span>=</span><span>"beforeLeave"</span></div></div><div><div><div>7</div></div><div><span>        </span><span>v-on:leave</span><span>=</span><span>"leave"</span></div></div><div><div><div>8</div></div><div><span>        </span><span>v-on:after-leave</span><span>=</span><span>"afterLeave"</span></div></div><div><div><div>9</div></div><div><span>        </span><span>v-on:leave-cancelled</span><span>=</span><span>"leaveCancelled"</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>        </span><span>&lt;!-- ... --&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;/</span><span>transition</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span>// ----</span></div></div><div><div><div>14</div></div><div><span>methods :{</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>// --------</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>// 进入中</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>// --------</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>beforeEnter: function (el) {</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>// 当与 CSS 结合使用时</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>// 回调函数 done 是可选的 enter:</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>enter: function(el, done) {</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>el.offsetWidth</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>//玄学操作,不加这个设置全JS样式时,动画不会有过渡效果出现,可以认为是这个操作会强制动画刷新</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>done()</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>//done()所代表的函数就是下一个结束afterEnter的函数,只有调用了这个回调函数才会执行下面的方法</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>afterEnter: function (el) {</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>enterCancelled: function (el) {</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>// --------</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>// 离开时</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>// --------</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>beforeLeave: function (el) {</span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>// 当与 CSS 结合使用时</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>// 回调函数 done 是可选的 leave:</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>function(el, done) {</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>//  ...</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>done()</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>afterLeave: function (el) {</span></div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>50</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>// leaveCancelled 只用于  v-show 中</span></div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>leaveCancelled: function (el) {</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>注意:</strong></p><ul>
<li>推荐对于仅使用 JavaScript 过渡的元素添加 <code>v-bind:css="false"</code>，Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响</li>
<li>当只用 JavaScript 过渡的时候，<strong>在 <code>enter</code> 和 <code>leave</code> 中必须使用 <code>done</code> 进行回调</strong>。否则，它们将被同步调用，过渡会立即完成。</li>
</ul></section>
<section><h2>多个元素过渡<a href="#多个元素过渡"><span>#</span></a></h2><p>对于原生标签可以使用 <code>v-if</code>和<code>v-else</code> 来实现多标签过渡效果</p><p><strong>注意</strong>&lt;当有相同标签名的元素切换时&gt;，需要通过 <code>key</code> 特性设置唯一的值来标记以让 Vue 区分它们，否则 Vue 为了效率只会替换相同标签内部的内容</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>transition</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>v-if</span><span>=</span><span>"isEditing"</span><span> </span><span>key</span><span>=</span><span>"save"</span><span>&gt;Save&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>v-else</span><span> </span><span>key</span><span>=</span><span>"edit"</span><span>&gt;Edit&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span>&lt;/</span><span>transition</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;!--也可以也可以通过给同一个元素的 key 特性设置不同的状态来代替 v-if 和 v-else--&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;</span><span>transition</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>v-bind:key</span><span>=</span><span>"isEditing"</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>{{ isEditing ? 'Save' : 'Edit' }}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span>&lt;/</span><span>transition</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>过渡模式</strong></p><p><strong>通过 mode 属性来定义多个元素过渡时的过渡模式</strong></p><ul>
<li><strong>in-out</strong>：新元素先进行过渡，完成之后当前元素过渡离开</li>
<li><strong>out-in</strong>：当前元素先进行过渡，完成之后新元素过渡进入</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>transition</span><span> </span><span>mode</span><span>=</span><span>"out-in"</span><span>&gt;&lt;/</span><span>transition</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>列表过渡<a href="#列表过渡"><span>#</span></a></h2><p><strong>渲染一整个列表时,如用 <code>v-for</code> 来渲染,就需要使用<code>&lt; transition-group&gt;</code> 组件</strong>、</p><p><strong>注意:</strong></p><ul>
<li>不同于 <code>&lt; transition&gt;</code>组件，<code>&lt; transition-group&gt;</code>组件会以一个真实元素呈现,默认为一个 <code>&lt;span&gt;</code>标签,可以通过 tag 特性更换为其他元素 **如:**内部是 <code>li</code> 列表,那么可以用<code>&lt;transition-grounp tag="ul"&gt;</code>来让该组件被渲染时表现为 <code>ul</code></li>
<li>过渡模式不可用，因为不再相互切换特有的元素</li>
<li>内部元素必须要有提供唯一的 <code>key</code> 属性值</li>
</ul><section><h3>添加与删除列表<a href="#添加与删除列表"><span>#</span></a></h3><p><strong>通过对一个<code>transiton-grounp</code>用过渡的类名和实现当个成员在过渡时的效果</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>style</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>    </span><span>.list-item</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>display: </span><span>inline-block</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>margin-right: </span><span><span>10</span><span>px</span></span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>.list-enter-active</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>.list-leave-active</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>transition: </span><span>all</span><span> </span><span><span>1</span><span>s</span></span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>    </span><span>.list-enter</span><span><span>,</span><span> </span></span><span>.list-leave-to</span></div></div><div><div><div>11</div></div><div><span>    </span><span>/* .list-leave-active for below version 2.1.8 */</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>opacity: </span><span>0</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>transform: </span><span>translateY</span><span>(</span><span><span>30</span><span>px</span></span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"list-demo"</span><span> </span><span>class</span><span>=</span><span>"demo"</span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>v-on:click</span><span>=</span><span>"add"</span><span>&gt;Add&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>v-on:click</span><span>=</span><span>"remove"</span><span>&gt;Remove&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>&lt;</span><span>transition-group</span><span> </span><span>name</span><span>=</span><span>"list"</span><span> </span><span>tag</span><span>=</span><span>"p"</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>&lt;</span><span>span</span><span> </span><span>v-for</span><span>=</span><span>"item in items"</span><span> </span><span>v-bind:key</span><span>=</span><span>"item"</span><span> </span><span>class</span><span>=</span><span>"list-item"</span><span>&gt;</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>{{ item }}</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>&lt;/</span><span>span</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>&lt;/</span><span>transition-group</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>28</div></div><div><span>    </span><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>29</div></div><div><span>        </span><span>el</span><span>:</span><span> </span><span>'#list-demo'</span><span>,</span></div></div><div><div><div>30</div></div><div><span>        </span><span>data</span><span>:</span><span> {</span></div></div><div><div><div>31</div></div><div><span>            </span><span>items</span><span>:</span><span> [</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>, </span><span>5</span><span>, </span><span>6</span><span>, </span><span>7</span><span>, </span><span>8</span><span>, </span><span>9</span><span>],</span></div></div><div><div><div>32</div></div><div><span>            </span><span>nextNum</span><span>:</span><span> </span><span>10</span><span>,</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>34</div></div><div><span>        </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>35</div></div><div><span>            </span><span>randomIndex</span><span>:</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>36</div></div><div><span>                </span><span>return</span><span><span> </span><span>Math</span><span>.</span></span><span>floor</span><span><span>(</span><span>Math</span><span>.</span></span><span>random</span><span>() </span><span>*</span><span> </span><span>this</span><span>.</span><span>items</span><span>.</span><span>length</span><span>)</span></div></div><div><div><div>37</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>38</div></div><div><span>            </span><span>add</span><span>:</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>39</div></div><div><span>                </span><span>this</span><span>.</span><span>items</span><span>.</span><span>splice</span><span>(</span><span>this</span><span>.</span><span>randomIndex</span><span>(), </span><span>0</span><span>, </span><span>this</span><span>.</span><span>nextNum</span><span>++</span><span>)</span></div></div><div><div><div>40</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>41</div></div><div><span>            </span><span>remove</span><span>:</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>42</div></div><div><span>                </span><span>this</span><span>.</span><span>items</span><span>.</span><span>splice</span><span>(</span><span>this</span><span>.</span><span>randomIndex</span><span>(), </span><span>1</span><span>)</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>46</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>排序过渡列表<a href="#排序过渡列表"><span>#</span></a></h3><p><strong>上方的添加与删除列表只会对进行操作的成员自身产生过渡效果，对组件中的其他成员没有过渡效果,如果要想通过过渡效果进行添加,需要使用 <code>v-move</code> 的类名写入 CSS 样式</strong>，该效果会在组件的成员改变其定位的时候起作用，所以要想起过渡效果还需要在过渡时将元素的定位改变为绝对定位，也可以通过 <code>name</code> 属性来自定义前缀，也能通过 <code>move-class</code> 属性 CSS 效果</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>style</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>    </span><span>.flip-list-move</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>transition: transform </span><span><span>1</span><span>s</span></span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"flip-list-demo"</span><span> </span><span>class</span><span>=</span><span>"demo"</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>v-on:click</span><span>=</span><span>"shuffle"</span><span>&gt;Shuffle&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>&lt;</span><span>transition-group</span><span> </span><span>name</span><span>=</span><span>"flip-list"</span><span> </span><span>tag</span><span>=</span><span>"ul"</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>&lt;</span><span>li</span><span> </span><span>v-for</span><span>=</span><span>"item in items"</span><span> </span><span>v-bind:key</span><span>=</span><span>"item"</span><span>&gt;{{ item }}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&lt;/</span><span>transition-group</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>18</div></div><div><span>        </span><span>el</span><span>:</span><span> </span><span>'#flip-list-demo'</span><span>,</span></div></div><div><div><div>19</div></div><div><span>        </span><span>data</span><span>:</span><span> {</span></div></div><div><div><div>20</div></div><div><span>            </span><span>items</span><span>:</span><span> [</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>, </span><span>5</span><span>, </span><span>6</span><span>, </span><span>7</span><span>, </span><span>8</span><span>, </span><span>9</span><span>],</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>22</div></div><div><span>        </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>23</div></div><div><span>            </span><span>shuffle</span><span>:</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>24</div></div><div><span>                </span><span>this</span><span>.</span><span>items</span><span> </span><span>=</span><span><span> </span><span>_</span><span>.</span></span><span>shuffle</span><span>(</span><span>this</span><span>.</span><span>items</span><span>)</span></div></div><div><div><div>25</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>28</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>交错过渡列表<a href="#交错过渡列表"><span>#</span></a></h3><p><strong>通过 JS 钩子来实现列表的过渡效果,使 <code>data</code> 属性与 JS 进行数据通信,就可以实现列表的交错过渡效果</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"staggered-list-demo"</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>input</span><span> </span><span>v-model</span><span>=</span><span>"query"</span><span> /&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>transition-group</span></div></div><div><div><div>6</div></div><div><span>        </span><span>name</span><span>=</span><span>"staggered-fade"</span></div></div><div><div><div>7</div></div><div><span>        </span><span>tag</span><span>=</span><span>"ul"</span></div></div><div><div><div>8</div></div><div><span>        </span><span>v-bind:css</span><span>=</span><span>"false"</span></div></div><div><div><div>9</div></div><div><span>        </span><span>v-on:before-enter</span><span>=</span><span>"beforeEnter"</span></div></div><div><div><div>10</div></div><div><span>        </span><span>v-on:enter</span><span>=</span><span>"enter"</span></div></div><div><div><div>11</div></div><div><span>        </span><span>v-on:leave</span><span>=</span><span>"leave"</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>&lt;</span><span>li</span></div></div><div><div><div>14</div></div><div><span>            </span><span>v-for</span><span>=</span><span>"(item, index) in computedList"</span></div></div><div><div><div>15</div></div><div><span>            </span><span>v-bind:key</span><span>=</span><span>"item.msg"</span></div></div><div><div><div>16</div></div><div><span>            </span><span>v-bind:data-index</span><span>=</span><span>"index"</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>&gt;</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>{{ item.msg }}</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>&lt;/</span><span>transition-group</span><span>&gt;</span></div></div><div><div><div>21</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span>    </span><span>new</span><span> </span><span>Vue</span><span>({</span></div></div><div><div><div>25</div></div><div><span>        </span><span>el</span><span>:</span><span> </span><span>'#staggered-list-demo'</span><span>,</span></div></div><div><div><div>26</div></div><div><span>        </span><span>data</span><span>:</span><span> {</span></div></div><div><div><div>27</div></div><div><span>            </span><span>query</span><span>:</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>28</div></div><div><span>            </span><span>list</span><span>:</span><span> [</span></div></div><div><div><div>29</div></div><div><span><span>                </span></span><span>{ </span><span>msg</span><span>:</span><span> </span><span>'Bruce Lee'</span><span> },</span></div></div><div><div><div>30</div></div><div><span><span>                </span></span><span>{ </span><span>msg</span><span>:</span><span> </span><span>'Jackie Chan'</span><span> },</span></div></div><div><div><div>31</div></div><div><span><span>                </span></span><span>{ </span><span>msg</span><span>:</span><span> </span><span>'Chuck Norris'</span><span> },</span></div></div><div><div><div>32</div></div><div><span><span>                </span></span><span>{ </span><span>msg</span><span>:</span><span> </span><span>'Jet Li'</span><span> },</span></div></div><div><div><div>33</div></div><div><span><span>                </span></span><span>{ </span><span>msg</span><span>:</span><span> </span><span>'Kung Fury'</span><span> },</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>],</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>36</div></div><div><span>        </span><span>computed</span><span>:</span><span> {</span></div></div><div><div><div>37</div></div><div><span>            </span><span>computedList</span><span>:</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>38</div></div><div><span>                </span><span>var</span><span><span> </span><span>vm</span><span> </span></span><span>=</span><span> </span><span>this</span></div></div><div><div><div>39</div></div><div><span>                </span><span>return</span><span> </span><span>this</span><span>.</span><span>list</span><span>.</span><span>filter</span><span>(</span><span>function</span><span><span> (</span><span>item</span><span>) {</span></span></div></div><div><div><div>40</div></div><div><span>                    </span><span>return</span><span> (</span></div></div><div><div><div>41</div></div><div><span><span>                        </span></span><span>item</span><span>.</span><span>msg</span></div></div><div><div><div>42</div></div><div><span><span>                            </span></span><span>.</span><span>toLowerCase</span><span>()</span></div></div><div><div><div>43</div></div><div><span><span>                            </span></span><span>.</span><span>indexOf</span><span><span>(</span><span>vm</span><span>.</span></span><span>query</span><span>.</span><span>toLowerCase</span><span>()) </span><span>!==</span><span> </span><span>-</span><span>1</span></div></div><div><div><div>44</div></div><div><span><span>                    </span></span><span>)</span></div></div><div><div><div>45</div></div><div><span><span>                </span></span><span>})</span></div></div><div><div><div>46</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>48</div></div><div><span>        </span><span>methods</span><span>:</span><span> {</span></div></div><div><div><div>49</div></div><div><span>            </span><span>beforeEnter</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>el</span><span>) {</span></span></div></div><div><div><div>50</div></div><div><span><span>                </span></span><span>el</span><span>.</span><span>style</span><span>.</span><span>opacity</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>51</div></div><div><span><span>                </span></span><span>el</span><span>.</span><span>style</span><span>.</span><span>height</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>53</div></div><div><span>            </span><span>enter</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>el</span><span>, </span><span>done</span><span>) {</span></span></div></div><div><div><div>54</div></div><div><span>                </span><span>var</span><span><span> </span><span>delay</span><span> </span></span><span>=</span><span><span> </span><span>el</span><span>.</span></span><span>dataset</span><span>.</span><span>index</span><span> </span><span>*</span><span> </span><span>150</span></div></div><div><div><div>55</div></div><div><span>                </span><span>setTimeout</span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>56</div></div><div><span>                    </span><span>Velocity</span><span>(</span></div></div><div><div><div>57</div></div><div><span><span>                        </span></span><span>el</span><span>,</span></div></div><div><div><div>58</div></div><div><span><span>                        </span></span><span>{ </span><span>opacity</span><span>:</span><span> </span><span>1</span><span>, </span><span>height</span><span>:</span><span> </span><span>'1.6em'</span><span> },</span></div></div><div><div><div>59</div></div><div><span><span>                        </span></span><span>{ </span><span>complete</span><span>:</span><span><span> </span><span>done</span><span> }</span></span></div></div><div><div><div>60</div></div><div><span><span>                    </span></span><span>)</span></div></div><div><div><div>61</div></div><div><span><span>                </span></span><span>}, </span><span>delay</span><span>)</span></div></div><div><div><div>62</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>63</div></div><div><span>            </span><span>leave</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>el</span><span>, </span><span>done</span><span>) {</span></span></div></div><div><div><div>64</div></div><div><span>                </span><span>var</span><span><span> </span><span>delay</span><span> </span></span><span>=</span><span><span> </span><span>el</span><span>.</span></span><span>dataset</span><span>.</span><span>index</span><span> </span><span>*</span><span> </span><span>150</span></div></div><div><div><div>65</div></div><div><span>                </span><span>setTimeout</span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>66</div></div><div><span>                    </span><span>Velocity</span><span>(</span></div></div><div><div><div>67</div></div><div><span><span>                        </span></span><span>el</span><span>,</span></div></div><div><div><div>68</div></div><div><span><span>                        </span></span><span>{ </span><span>opacity</span><span>:</span><span> </span><span>0</span><span>, </span><span>height</span><span>:</span><span> </span><span>0</span><span> },</span></div></div><div><div><div>69</div></div><div><span><span>                        </span></span><span>{ </span><span>complete</span><span>:</span><span><span> </span><span>done</span><span> }</span></span></div></div><div><div><div>70</div></div><div><span><span>                    </span></span><span>)</span></div></div><div><div><div>71</div></div><div><span><span>                </span></span><span>}, </span><span>delay</span><span>)</span></div></div><div><div><div>72</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>73</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>74</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>75</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/promise/</id>
      <title type="text">Promise</title>
      <published>2022-10-04T07:48:08.000Z</published>
      <updated>2022-10-04T07:48:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/promise/"/>
      <summary type="text">Promise 是异步编程的一种解决方案，比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现，ES6 将其写进了语言标准，统一了用法，原生提供了Promise对象。 所谓Promise，简单说就是一个容器，里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 从语…</summary>
      <content type="html"><![CDATA[<section><h2>Promise 是什么<a href="#promise-是什么"><span>#</span></a></h2><section><h3>理解 Promise<a href="#理解-promise"><span>#</span></a></h3><p>Promise 是异步编程的一种解决方案，比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现，ES6 将其写进了语言标准，统一了用法，原生提供了Promise对象。</p><p>所谓Promise，简单说就是一个容器，里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。</p><ul>
<li>从语法上看：<code>Promise</code>是一个构造函数 (自己身上有<code>all</code>、<code>reject</code>、<code>resolve</code>这几个方法，原型上有<code>then</code>、<code>catch</code>等方法)</li>
<li>功能上看：<code>promise</code>对象用来封装一个异步操作并可以获取其成功/失败的结果值</li>
</ul></section><section><h3>Promise 的三种状态<a href="#promise-的三种状态"><span>#</span></a></h3><p>实例对象promise中的一个属性 <code>PromiseState</code>，用于存放 <code>Promise</code> 的三种状态 <code>pending</code> <code>resolved/fullfilled</code> <code>rejected</code></p><ol>
<li><code>pending</code> 变为 <code>resolved</code>/<code>fullfilled</code></li>
<li><code>pending</code> 变为 <code>rejected</code></li>
</ol><p>注：</p><ul>
<li>对象的状态不受外界影响</li>
<li>只有这两种，且一个 <code>promise</code> 对象只能改变一次</li>
<li>一旦状态改变，就不会再变，任何时候都可以得到这个结果</li>
<li>无论成功还是失败，都会有一个结果数据。成功的结果数据一般称为 <code>value</code>，而失败的一般称为 <code>reason</code>。</li>
</ul></section><section><h3>Promise对象的值<a href="#promise对象的值"><span>#</span></a></h3><p>实例对象<code>promise</code>的另一个值 <code>PromiseResult</code>
保存着对象 成功/失败 的值（<code>value</code>/<code>reason</code>）</p></section><section><h3>Promise 的基本使用<a href="#promise-的基本使用"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>promise</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Promise</span><span>(</span><span>function</span><span><span> (</span><span>resolve</span><span>, </span><span>reject</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>// ... some code</span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span> (</span><span>/* 异步操作成功 */</span><span> ) {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>resolve</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>reject</span><span><span>(</span><span>reason</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Promise</code>构造函数接受<strong>一个函数</strong>（执行器函数）作为参数，该函数的<strong>两个参数</strong>分别是<code>resolve</code>和<code>reject</code>。它们是<strong>两个函数</strong>，由 JavaScript 引擎提供，不用自己部署。</p><p><code>resolve</code>函数的作用是，将<code>Promise</code>对象的状态从“未完成”变为“成功”（即从 <code>pending</code> 变为 <code>resolved</code>），在<strong>异步操作成功</strong>时调用，并将异步操作的结果，作为参数<code>value</code>传递出去；
<code>reject</code>函数的作用是，将<code>Promise</code>对象的状态从“未完成”变为“失败”（即从 <code>pending</code> 变为 <code>rejected</code>），在<strong>异步操作失败</strong>时调用，并将异步操作报出的错误，作为参数<code>error</code>/<code>reason</code>传递出去。</p><p><code>Promise</code>实例生成以后，可以用<code>then</code>方法分别指定<code>resolved</code>状态和<code>rejected</code>状态的回调函数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Promise.then(</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>function (value) {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>// success</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>function (reason) {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>// failure</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>then</code>方法可以接受<strong>两个回调函数</strong>作为参数。</p><ul>
<li>第一个回调函数<code>onResolved()</code>是<code>Promise</code>对象的状态变为<code>resolved</code>时调用</li>
<li>第二个回调函数<code>onRejected()</code>是<code>Promise</code>对象的状态变为<code>rejected</code>时调用</li>
<li>这两个函数都是可选的，不一定要提供。它们都接受<code>Promise</code>对象传出的值作为参数</li>
</ul><p>例：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建一个新的p对象promise</span></div></div><div><div><div>2</div></div><div><span>const p = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// 执行器函数</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 执行异步操作任务</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>setTimeout(() =&gt; {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>const time = Date.now()</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>// 如果当前时间是偶数代表成功，否则失败</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>if (time % 2 == 0) {</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>// 如果成功，调用resolve(value)</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>resolve('成功的数据，time=' + time)</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>} else {</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>// 如果失败，调用reject(reason)</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>reject('失败的数据，time=' + time)</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}, 1000)</span></div></div><div><div><div>16</div></div><div><span>})</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>p.then(</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>(value) =&gt; {</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>// 接收得到成功的value数据 onResolved</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>console.log('成功的回调', value) // 成功的回调 成功的数据，time=1615015043258</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>// 接收得到失败的reason数据 onRejected</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>console.log('失败的回调', reason) // 失败的回调 失败的数据，time=1615014995315</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><blockquote><p>.then() 和执行器(executor)同步执行，.then() 中的回调函数异步执行</p></blockquote></section></section>
<section><h2>为什么要使用 Promise<a href="#为什么要使用-promise"><span>#</span></a></h2><section><h3>指定回调函数的方式更加灵活<a href="#指定回调函数的方式更加灵活"><span>#</span></a></h3><p>ES5：必须在启动异步任务前指定</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 纯回调的形式</span></div></div><div><div><div>2</div></div><div><span>// 成功的回调函数</span></div></div><div><div><div>3</div></div><div><span>function successCallback(result) {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console.log('声音文件创建成功：' + result)</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>// 失败的回调函数</span></div></div><div><div><div>7</div></div><div><span>function failureCallback(error) {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console.log('声音文件创建失败：' + error)</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div><span>// 必须先指定回调函数，再执行异步任务</span></div></div><div><div><div>11</div></div><div><span>createAudioFileAsync(audioSettings, successCallback, failureCallback)</span></div></div><div><div><div>12</div></div><div><span>// 回调函数在执行异步任务（函数）前就要指定</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>promise：启动异步任务 =&gt; 返回promise对象 =&gt; 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 2. 使用Promise</span></div></div><div><div><div>2</div></div><div><span>const promise = createAudioFileAsync(audioSettings) // 执行2秒</span></div></div><div><div><div>3</div></div><div><span>setTimeout(() =&gt; {</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>promise.then(successCallback, failureCallback) // 也可以获取</span></div></div><div><div><div>5</div></div><div><span>}, 3000)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>支持链式调用，可以解决回调地狱问题<a href="#支持链式调用可以解决回调地狱问题"><span>#</span></a></h3><section><h4>什么是回调地狱<a href="#什么是回调地狱"><span>#</span></a></h4><blockquote><p>回调函数嵌套调用，外部回调函数异步执行的结果是其内部嵌套的回调函数执行的条件</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>doSomething(function(result) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>doSomethingElse(result, function(newResult) {</span></div></div><div><div><div>3</div></div><div><span><span>       </span></span><span>doThirdThing(newResult, function(finalResult) {</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>console.log('Got the final result:' + finalResult)</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>}, failureCallback)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}, failureCallback)</span></div></div><div><div><div>7</div></div><div><span>}, failureCallback)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>回调地狱的缺点</strong></p><ol>
<li>不便于阅读</li>
<li>不便于异常处理</li>
</ol></section><section><h4>使用 promise 的链式调用解决回调地狱<a href="#使用-promise-的链式调用解决回调地狱"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>doSomething()</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>.then((result) =&gt; doSomethingElse(result))</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>.then((newResult) =&gt; doThirdThing(newResult))</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>.then((finalResult) =&gt; {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>console.log('Got the final result:' + finalResult)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>.catch(failureCallback)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>回调地狱的终极解决方案 async/await<a href="#回调地狱的终极解决方案-asyncawait"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>async function request() {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>try {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>const result = await doSomething()</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>const newResult = await doSomethingElse(result)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>const finalResult = await doThirdThing(newResult)</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>console.log('Got the final result:' + finalResult)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>} catch (error) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>failureCallback(error)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>Promise 相关方法<a href="#promise-相关方法"><span>#</span></a></h2><section><h3>Promise 构造函数：<code>Promise(executor) {}</code><a href="#promise-构造函数promiseexecutor-"><span>#</span></a></h3><ul>
<li><code>executor</code> 函数：<strong>同步执行</strong> <code>(resolve, reject) =&gt; {}</code></li>
<li><code>resolve</code> 函数：内部定义成功时调用的函数 <code>resove(value)</code></li>
<li><code>reject</code> 函数：内部定义失败时调用的函数 <code>reject(reason)</code></li>
</ul><p><code>Promise</code>构造函数接受一个函数作为参数，该函数的两个参数分别是<code>resolve</code>和<code>reject</code>。它们是两个函数，由 JavaScript 引擎提供，不用自己部署。</p><p><code>resolve</code>函数的作用是，将<code>Promise</code>对象的状态从“未完成”变为“成功”(即从 <code>pending</code> 变为 <code>resolved</code>)，在异步操作成功时调用，并将异步操作的结果，作为参数传递出去</p><p><code>reject</code>函数的作用是，将<code>Promise</code>对象的状态从“未完成”变为“失败”(即从 <code>pending</code> 变为 <code>rejected</code>)，在异步操作失败时调用，并将异步操作报出的错误，作为参数传递出去。</p><blockquote><p><code>executor</code> 是执行器，会在 <code>Promise</code> 内部立即同步回调，异步操作 <code>resolve</code>/<code>reject</code> 就在 <code>executor</code> 中执行</p></blockquote></section><section><h3>Promise.prototype.then ()<a href="#promiseprototypethen-"><span>#</span></a></h3><p><code>p.then(onResolved, onRejected)</code></p><p>指定两个回调（成功+失败）</p><ul>
<li><code>onResolved</code> 函数：成功的回调函数 <code>(value) =&gt; {}</code></li>
<li><code>onRejected</code> 函数：失败的回调函数 <code>(reason) =&gt; {}</code></li>
</ul><p><code>then()</code>的作用是为 <code>Promise</code> 实例添加状态改变时的回调函数。<code>then</code>方法的第一个参数是<code>resolved</code>状态的回调函数，第二个参数(可选)是<code>rejected</code>状态的回调函数。</p><p><code>then</code>方法返回的是一个新的<code>Promise</code>实例(注意，不是原来那个<code>Promise</code>实例)。因此可以采用链式写法，即<code>then</code>方法后面再调用另一个<code>then</code>方法。</p><blockquote><p>指定用于得到成功 <code>value</code> 的成功回调和用于得到失败 <code>reason</code> 的失败回调，返回一个新的 <code>promise</code> 对象</p></blockquote></section><section><h3>Promise.prototype.catch()<a href="#promiseprototypecatch"><span>#</span></a></h3><p><code>p.catch(onRejected)</code></p><p>指定失败的回调</p><ul>
<li><code>onRejected</code> 函数：失败的回调函数 <code>(reason) =&gt; {}</code></li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>, </span><span>reject</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>// excutor执行器函数</span></div></div><div><div><div>3</div></div><div><span>    </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>if</span><span> (</span><span>/* 异步操作成功 */</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>            </span><span>resolve</span><span>(</span><span>'成功的数据'</span><span>) </span><span>// resolve()函数</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>7</div></div><div><span>            </span><span>reject</span><span>(</span><span>'失败的数据'</span><span>) </span><span>//reject()函数</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}, </span><span>1000</span><span>)</span></div></div><div><div><div>10</div></div><div><span>})</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>.</span><span>then</span><span><span>((</span><span>value</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>// onResolved()函数</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>value</span><span>) </span></span><span>// 成功的数据</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>.</span><span>catch</span><span><span>((</span><span>reason</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>// onRejected()函数</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>reason</span><span>) </span></span><span>// 失败的数据</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><blockquote><p>这是<code>then()</code> 的语法糖，相当于 <code>then(undefined, onRejected)</code></p></blockquote></section><section><h3>Promise.resolve()<a href="#promiseresolve"><span>#</span></a></h3><p><code>Promise.resolve(value)</code></p><ul>
<li>
<p><code>value</code>：将被 <code>Promise</code> 对象解析的参数，也可以是一个成功或失败的 <code>Promise</code> 对象</p>
</li>
<li>
<p><code>return</code> ：返回一个带着给定值解析过的 <code>Promise</code> 对象，如果参数本身就是一个 <code>Promise</code> 对象，则直接返回这个 <code>Promise</code> 对象。</p>
</li>
</ul><p><code>Promise.resolve</code>方法的参数分成四种情况。</p><ol>
<li>
<p>参数是一个<code>Promise</code>实例</p>
<p>如果参数是 Promise 实例，那么<code>Promise.resolve</code>将不做任何修改、原封不动地返回这个实例。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p2 = Promise.resolve(</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>// resolve('OK') // 成功的Promise</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>reject('Error')</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>6</div></div><div><span>)</span></div></div><div><div><div>7</div></div><div><span>console.log(p2) // Promise {&lt;rejected&gt;: 'Error'}</span></div></div><div><div><div>8</div></div><div><span>p2.catch((reason) =&gt; {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console.log(reason) // Error</span></div></div><div><div><div>10</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>参数是一个<code>thenable</code>对象</p>
<p><code>thenable</code>对象指的是具有<code>then</code>方法的对象，比如下面这个对象。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>thenable</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>then</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>resolve</span><span>, </span><span>reject</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>        </span><span>resolve</span><span>(</span><span>42</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p><code>Promise.resolve</code>方法会将这个对象转为 Promise 对象，然后就立即执行<code>thenable</code>对象的<code>then</code>方法。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>thenable</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>then</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>resolve</span><span>, </span><span>reject</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>resolve</span><span>(</span><span>42</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>p1</span><span> </span></span><span>=</span><span> </span><span>Promise</span><span>.</span><span>resolve</span><span><span>(</span><span>thenable</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span><span>p1</span><span>.</span></span><span>then</span><span>(</span><span>function</span><span><span> (</span><span>value</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>value</span><span>); </span></span><span>// 42</span></div></div><div><div><div>10</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，<code>thenable</code>对象的<code>then</code>方法执行后，对象<code>p1</code>的状态就变为<code>resolved</code>，从而立即执行最后那个<code>then</code>方法指定的回调函数，输出 42。</p>
</li>
<li>
<p>参数不是具有<code>then</code>方法的对象，或根本就不是对象</p>
<p>如果参数是一个原始值，或者是一个不具有<code>then</code>方法的对象，则<code>Promise.resolve</code>方法返回一个新的 Promise 对象，状态为<code>resolved</code>。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>p</span><span> </span><span>=</span><span> </span><span>Promise</span><span>.</span><span>resolve</span><span>(</span><span>"Hello"</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>p</span><span>.</span></span><span>then</span><span>(</span><span>function</span><span><span> (</span><span>s</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>s</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span>});</span></div></div><div><div><div>6</div></div><div><span>// Hello</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码生成一个新的 Promise 对象的实例<code>p</code>。由于字符串<code>Hello</code>不属于异步操作(判断方法是字符串对象不具有 then 方法)，返回 Promise 实例的状态从一生成就是<code>resolved</code>，所以回调函数会立即执行。<code>Promise.resolve</code>方法的参数，会同时传给回调函数。</p>
</li>
<li>
<p>不带有任何参数</p>
<p><code>Promise.resolve</code>方法允许调用时不带参数，直接返回一个<code>resolved</code>状态的 Promise 对象。</p>
<p>所以，如果希望得到一个 Promise 对象，比较方便的方法就是直接调用<code>Promise.resolve</code>方法。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>p</span><span> </span><span>=</span><span> </span><span>Promise</span><span>.</span><span>resolve</span><span>();</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>p</span><span>.</span></span><span>then</span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>5</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码的变量<code>p</code>就是一个 Promise 对象。</p>
<p>需要注意的是，立即<code>resolve</code>的 Promise 对象，是在本轮“事件循环”(event loop)的结束时，而不是在下一轮“事件循环”的开始时。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setTimeout</span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"three"</span><span>)</span></div></div><div><div><div>3</div></div><div><span>}, </span><span>0</span><span>)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>Promise</span><span>.</span><span>resolve</span><span>().</span><span>then</span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"two"</span><span>)</span></div></div><div><div><div>7</div></div><div><span>})</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>"one"</span><span>)</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// one</span></div></div><div><div><div>12</div></div><div><span>// two</span></div></div><div><div><div>13</div></div><div><span>// three</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，<code>setTimeout(fn, 0)</code>在下一轮“事件循环”开始时执行，<code>Promise.resolve()</code>在本轮“事件循环”结束时执行，<code>console.log('one')</code>则是立即执行，因此最先输出</p>
</li>
</ol></section><section><h3>Promise.reject()<a href="#promisereject"><span>#</span></a></h3><p><code>Promise.resolve(reason)</code></p><ul>
<li><code>reason</code>：失败的原因</li>
<li><code>return</code>：返回一个失败的 <code>promise</code> 对象，该实例的状态为<code>rejected</code>。</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p = Promise.reject(521)</span></div></div><div><div><div>2</div></div><div><span>let p2 = Promise.reject('iloveyou')</span></div></div><div><div><div>3</div></div><div><span>let p3 = Promise.reject(</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>resolve('OK')</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>console.log(p) // Promise {&lt;rejected&gt;: 521}</span></div></div><div><div><div>10</div></div><div><span>console.log(p2) // Promise {&lt;rejected&gt;: 'iloveyou'}</span></div></div><div><div><div>11</div></div><div><span>console.log(p3) // Promise {&lt;rejected&gt;: Promise}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注：</p><ul>
<li><code>Promise.resolve()</code>/<code>Promise.reject()</code> 方法就是一个语法糖</li>
<li>用来快速得到<code>Promise</code>对象</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//产生一个成功值为1的promise对象</span></div></div><div><div><div>2</div></div><div><span>new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>resolve(1)</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>//相当于</span></div></div><div><div><div>6</div></div><div><span>const p1 = Promise.resolve(1)</span></div></div><div><div><div>7</div></div><div><span>const p2 = Promise.resolve(2)</span></div></div><div><div><div>8</div></div><div><span>const p3 = Promise.reject(3)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>p1.then((value) =&gt; {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console.log(value)</span></div></div><div><div><div>12</div></div><div><span>}) // 1</span></div></div><div><div><div>13</div></div><div><span>p2.then((value) =&gt; {</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>console.log(value)</span></div></div><div><div><div>15</div></div><div><span>}) // 2</span></div></div><div><div><div>16</div></div><div><span>p3.catch((reason) =&gt; {</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>console.log(reason)</span></div></div><div><div><div>18</div></div><div><span>}) // 3</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Promise.all()<a href="#promiseall"><span>#</span></a></h3><p><code>Promise.all(iterable)</code></p><ul>
<li><code>iterable</code>：包含 n 个 <code>promise</code> 的可迭代对象，如 <code>Array</code> 或 <code>String</code></li>
<li><code>return</code>：返回一个新的 <code>promise</code>，只有所有的 <code>promise</code> 都成功才成功，只要有一个失败了就直接失败</li>
</ul><p>成功：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p1 = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>resolve('OK')</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div><div><div><div>4</div></div><div><span>let p2 = Promise.resolve('Success')</span></div></div><div><div><div>5</div></div><div><span>let p3 = Promise.resolve('Oh Yeah')</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>const result = Promise.all([p1, p2, p3])</span></div></div><div><div><div>8</div></div><div><span>console.log(result) // Promise {&lt;pending&gt;}</span></div></div><div><div><div>9</div></div><div><span><span>                       </span></span><span>[[PromiseResult]]: Array(3)</span></div></div><div><div><div>10</div></div><div><span><span>                        </span></span><span>0: "OK"</span></div></div><div><div><div>11</div></div><div><span><span>                        </span></span><span>1: "Success"</span></div></div><div><div><div>12</div></div><div><span><span>                        </span></span><span>2: "Oh Yeah"</span></div></div><div><div><div>13</div></div><div><span><span>                        </span></span><span>length: 3</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>失败：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p1 = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>resolve('OK')</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div><div><div><div>4</div></div><div><span>let p2 = Promise.reject('Error')</span></div></div><div><div><div>5</div></div><div><span>let p3 = Promise.resolve('Oh Yeah')</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>const result = Promise.all([p1, p2, p3])</span></div></div><div><div><div>8</div></div><div><span>console.log(result) //Promise {&lt;pending&gt;}</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>[[PromiseResult]]: "Error"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Promise.race()<a href="#promiserace"><span>#</span></a></h3><p><code>Promise.race(iterable)</code></p><ul>
<li><code>iterable</code>：包含 n 个 <code>promise</code> 的可迭代对象，如 <code>Array</code> 或 <code>String</code></li>
<li><code>return</code>：返回一个新的 <code>promise</code>，第一个完成的 <code>promise</code> 的结果状态就是最终的结果状态
<strong>谁先完成就输出谁(不管是成功还是失败)</strong></li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const pRace = Promise.race([p1, p2, p3])</span></div></div><div><div><div>2</div></div><div><span>// 谁先完成就输出谁(不管是成功还是失败)</span></div></div><div><div><div>3</div></div><div><span>const p1 = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>setTimeout(() =&gt; {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>resolve(1)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}, 1000)</span></div></div><div><div><div>7</div></div><div><span>})</span></div></div><div><div><div>8</div></div><div><span>const p2 = Promise.resolve(2)</span></div></div><div><div><div>9</div></div><div><span>const p3 = Promise.reject(3)</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>pRace.then(</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>(value) =&gt; {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>console.log('race onResolved()', value)</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>console.log('race onRejected()', reason)</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>)</span></div></div><div><div><div>19</div></div><div><span>//race onResolved() 2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Promise.prototype.finally()<a href="#promiseprototypefinally"><span>#</span></a></h3><p><code>finally()</code> 方法返回一个<code>Promise</code>。在<code>promise</code>结束时，无论结果是<code>resolved</code>或者是<code>rejected</code>，都会执行指定的回调函数。这为在<code>Promise</code>是否成功完成后都需要执行的代码提供了一种方式，避免了同样的语句需要在<code>then()</code>和<code>catch()</code>中各写一次的情况。</p><p><code>finally</code>本质上是<code>then</code>方法的特例。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>promise.finally(() =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 语句</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 等同于</span></div></div><div><div><div>6</div></div><div><span>promise.then(</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>(result) =&gt; {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>// 语句</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>return result</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>(error) =&gt; {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>// 语句</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>throw error</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>实例：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// resolve 的值是 undefined</span></div></div><div><div><div>2</div></div><div><span>Promise.resolve(2).then(</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>() =&gt; {},</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>() =&gt; {}</span></div></div><div><div><div>5</div></div><div><span>)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// resolve 的值是 2</span></div></div><div><div><div>8</div></div><div><span>Promise.resolve(2).finally(() =&gt; {})</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// reject 的值是 undefined</span></div></div><div><div><div>11</div></div><div><span>Promise.reject(3).then(</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>() =&gt; {},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>() =&gt; {}</span></div></div><div><div><div>14</div></div><div><span>)</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// reject 的值是 3</span></div></div><div><div><div>17</div></div><div><span>Promise.reject(3).finally(() =&gt; {})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>Promise 的相关问题<a href="#promise-的相关问题"><span>#</span></a></h2><section><h3>如何改变 promise 的状态<a href="#如何改变-promise-的状态"><span>#</span></a></h3><ol>
<li><code>resolve(value)</code>：如果当前是 <code>pending</code> 就会变为 `resolved“</li>
<li>“reject(reason)<code>：如果当前是 </code>pending<code>就会变为</code>rejected`</li>
<li>抛出异常：如果当前是 <code>pending</code> 就会变为 <code>rejected</code></li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const p = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>//resolve(1) // promise变为resolved成功状态</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>//reject(2) // promise变为rejected失败状态</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>throw new Error('出错了') // 抛出异常，promise变为rejected失败状态，reason为抛出的error</span></div></div><div><div><div>5</div></div><div><span>})</span></div></div><div><div><div>6</div></div><div><span>p.then(</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>(value) =&gt; {},</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console.log('reason', reason)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>)</span></div></div><div><div><div>12</div></div><div><span>// reason Error</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>一个 promise 指定多个成功/失败回调函数，都会调用吗<a href="#一个-promise-指定多个成功失败回调函数都会调用吗"><span>#</span></a></h3><p>当 <code>promise</code> <strong>改变</strong>为对应状态时<strong>都会调用</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const p = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>//resolve(1)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>reject(2)</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>p.then(</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>(value) =&gt; {},</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>console.log('reason', reason)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>)</span></div></div><div><div><div>11</div></div><div><span>p.then(</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>(value) =&gt; {},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>console.log('reason2', reason)</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>)</span></div></div><div><div><div>17</div></div><div><span>// reason 2</span></div></div><div><div><div>18</div></div><div><span>// reason2 2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>改变 promise 状态和指定回调函数谁先谁后<a href="#改变-promise-状态和指定回调函数谁先谁后"><span>#</span></a></h3><blockquote><p>都有可能，常规是先指定回调再改变状态，但也可以先改状态再指定回调</p></blockquote><ul>
<li>
<p>如何先改状态再指定回调</p>
<ol>
<li>
<p>在执行器中直接调用 <code>resolve()</code>/<code>reject()</code></p>
</li>
<li>
<p>延迟更长时间才调用 <code>then()</code></p>
</li>
</ol>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span>// setTimeout(() =&gt; {</span></div></div><div><div><div>3</div></div><div><span>resolve('OK')</span></div></div><div><div><div>4</div></div><div><span>// }, 1000); // 有异步就先指定回调，否则先改变状态</span></div></div><div><div><div>5</div></div><div><span>})</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>p.then(</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>(value) =&gt; {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console.log(value)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>(reason) =&gt; {}</span></div></div><div><div><div>12</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>什么时候才能得到数据？</p>
<ol>
<li>如果先指定的回调，那当状态发生改变时，回调函数就会调用得到数据</li>
<li>如果先改变的状态，那当指定回调时，回调函数就会调用得到数据</li>
</ol>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>setTimeout(() =&gt; {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>resolve(1) // 改变状态</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}, 1000)</span></div></div><div><div><div>5</div></div><div><span>}).then(</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// 指定回调函数 （先指定）</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>(value) =&gt; {},</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>(reason) =&gt; {}</span></div></div><div><div><div>9</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>此时，先指定回调函数，保存当前指定的回调函数；后改变状态(同时指定数据)，然后异步执行之前保存的回调函数。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>resolve(1) // 改变状态</span></div></div><div><div><div>3</div></div><div><span>}).then(</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 指定回调函数</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>(value) =&gt; {},</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>(reason) =&gt; {}</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>这种写法，先改变的状态(同时指定数据)，后指定回调函数(不需要再保存)，直接异步执行回调函数</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>###  promise.then() 返回的新 promise 的结果状态由什么决定</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>- 简单表达：由 `then()` 指定的回调函数执行的结果决定</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>let p = new Promise((resolve, reject) =&gt; {
resolve(‘ok’)
})
//执行 then 方法
let result = p.then(
(value) =&gt; {
console.log(value) //ok
},
(reason) =&gt; {
console.warn(reason)
}
)
console.log(result)
// Promise {}
// <a href="/posts/Prototype/">Prototype</a>: Promise
// <a href="/posts/PromiseState/">PromiseState</a>: “fulfilled”
// <a href="/posts/PromiseResult/">PromiseResult</a>: undefined</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 详细表达</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>1. 如果抛出异常，新 `promise` 变为 `rejected`，`reason` 为抛出的异常</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>let p = new Promise((resolve, reject) =&gt; {
resolve(‘ok’)
})
//执行 then 方法
let result = p.then(
(value) =&gt; {
//1. 抛出错误
throw ‘出了问题’
},
(reason) =&gt; {
console.warn(reason)
}
)
console.log(result)
// Promise {}
<a href="/posts/Prototype/">Prototype</a>: Promise
<a href="/posts/PromiseState/">PromiseState</a>: “rejected”
<a href="/posts/PromiseResult/">PromiseResult</a>: “出了问题</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>2. 如果返回的是非 `promise` 的任意值，新 `promise` 变为 `resolved`，`value` 为返回的值</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>resolve('ok')</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div><div><div><div>4</div></div><div><span>//执行 then 方法</span></div></div><div><div><div>5</div></div><div><span>let result = p.then(</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>(value) =&gt; {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>//2. 返回结果是非 Promise 类型的对象</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>return 521</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>console.warn(reason)</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>console.log(result)</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>// Promise {&lt;pending&gt;}</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>[[Prototype]]: Promise</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>[[PromiseState]]: "fulfilled"</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>[[PromiseResult]]: 521</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>3. 如果返回的是另一个新 `promise`，此 `promise` 的结果就会成为新 `promise` 的结果</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let p = new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>resolve('ok')</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div><div><div><div>4</div></div><div><span>//执行 then 方法</span></div></div><div><div><div>5</div></div><div><span>let result = p.then(</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>(value) =&gt; {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>//3. 返回结果是 Promise 对象</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>return new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>// resolve('success');</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>reject('error')</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>console.warn(reason)</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>)</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>console.log(result)</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>// Promise {&lt;pending&gt;}</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>[[Prototype]]: Promise</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>[[PromiseState]]: "rejected"</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>[[PromiseResult]]: "error"</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>例：</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>resolve(1)</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>.then(</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>(value) =&gt; {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>console.log('onResolved1()', value)</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>//return 2                   // onResolved2() 2</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>//return Promise.resolve(3)  // onResolved2() 3</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>//return Promise.reject(4)   // onRejected2() 4</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>//throw 5                    // onRejected2() 5</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>console.log('onRejected1()', reason)</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>.then(</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>(value) =&gt; {</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>console.log('onResolved2()', value)</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>(reason) =&gt; {</span></div></div><div><div><div>21</div></div><div><span><span>            </span></span><span>console.log('onRejected2()', reason)</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>24</div></div><div><span>// onResolved1() 1</span></div></div><div><div><div>25</div></div><div><span>// onResolved2() undefined</span></div></div><div><div><div>26</div></div><div><span>// Promise {&lt;fulfilled&gt;: undefined}</span></div></div><div><div><div>27</div></div><div><span>// 对应输出如上所示</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>​</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### promise 如何串联多个操作任务？</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>1. `promise` 的 `then()` 返回一个新的 `promise`，可以并成 `then()` 的链式调用</span></div></div><div><div><div>6</div></div><div><span>2. 通过 `then` 的链式调用串联多个同步/异步任务</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>let p = new Promise((resolve, reject) =&gt; {
setTimeout(() =&gt; {
resolve(‘OK’)
}, 1000)
})</p><p>p.then((value) =&gt; {
return new Promise((resolve, reject) =&gt; {
console.log(‘value’) // OK
resolve(‘success’)
})
})
.then((value) =&gt; {
console.log(value) // success
})
.then((value) =&gt; {
console.log(value) // undefined
})
// OK success undefined</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>例：</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>new Promise((resolve, reject) =&gt; {
setTimeout(() =&gt; {
console.log(‘执行任务1(异步)’)
resolve(1)
}, 1000)
})
.then((value) =&gt; {
console.log(‘任务1的结果’, value)
console.log(‘执行任务2(同步)’)
return 2 // 同步任务直接return返回结果
})
.then((value) =&gt; {
console.log(‘任务2的结果’, value)
return new Promise((resolve, reject) =&gt; {
// 异步任务需要包裹在Promise对象中
setTimeout(() =&gt; {
console.log(‘执行任务3(异步)’)
resolve(3)
}, 1000)
})
})
.then((value) =&gt; {
console.log(‘任务3的结果’, value)
})
// 执行任务1(异步)
// 任务1的结果 1
// 执行任务2(同步)
// 任务2的结果 2
// 执行任务3(异步)
// 任务3的结果 3</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>### Promise 异常穿透(传透)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>1. 当使用 `promise` 的 `then` 链式调用时，可以在最后指定失败的回调</span></div></div><div><div><div>4</div></div><div><span>2. 前面任何操作出了异常，都会传到最后失败的回调中处理</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>new Promise((resolve, reject) =&gt; {
//resolve(1)
reject(1)
})
.then((value) =&gt; {
console.log(‘onResolved1()’, value)
return 2
})
.then((value) =&gt; {
console.log(‘onResolved2()’, value)
return 3
})
.then((value) =&gt; {
console.log(‘onResolved3()’, value)
})
.catch((reason) =&gt; {
console.log(‘onRejected1()’, reason)
})
// onRejected1() 1</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>相当于这种写法：多写了很多`reason =&gt; {throw reason}`</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>new Promise((resolve, reject) =&gt; {
//resolve(1)
reject(1)
})
.then(
(value) =&gt; {
console.log(‘onResolved1()’, value)
return 2
},
(reason) =&gt; {
throw reason
} // 抛出失败的结果reason
)
.then(
(value) =&gt; {
console.log(‘onResolved2()’, value)
return 3
},
(reason) =&gt; {
throw reason
} // 抛出失败的结果reason
)
.then(
(value) =&gt; {
console.log(‘onResolved3()’, value)
},
(reason) =&gt; {
throw reason
} // 抛出失败的结果reason
)
.catch((reason) =&gt; {
console.log(‘onRejected1()’, reason)
})
// onRejected1() 1</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>所以失败的结果是一层一层处理下来的，最后传递到 `catch` 中。</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>注：将 `reason =&gt; {throw reason}` 替换为 `reason =&gt; Promise.reject(reason)` 也是一样的</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>### 中断 promise 链</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>当使用 `promise` 的 `then` 链式调用时，在中间中断，不再调用后面的回调函数</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>办法：在回调函数中返回一个 `pending` 状态的 `promise` 对象</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>new Promise((resolve, reject) =&gt; {
//resolve(1)
reject(1)
})
.then((value) =&gt; {
console.log(‘onResolved1()’, value)
return 2
})
.then((value) =&gt; {
console.log(‘onResolved2()’, value)
return 3
})
.then((value) =&gt; {
console.log(‘onResolved3()’, value)
})
.catch((reason) =&gt; {
console.log(‘onRejected1()’, reason)
})
.then(
(value) =&gt; {
console.log(‘onResolved4()’, value)
},
(reason) =&gt; {
console.log(‘onRejected2()’, reason)
}
)
// onRejected1() 1
// onResolved4() undefined</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>为了在 `catch` 中就中断执行，可以这样写：</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>new Promise((resolve, reject) =&gt; {
//resolve(1)
reject(1)
})
.then((value) =&gt; {
console.log(‘onResolved1()’, value)
return 2
})
.then((value) =&gt; {
console.log(‘onResolved2()’, value)
return 3
})
.then((value) =&gt; {
console.log(‘onResolved3()’, value)
})
.catch((reason) =&gt; {
console.log(‘onRejected1()’, reason)
return new Promise(() =&gt; {}) // 返回一个pending的promise
})
.then(
(value) =&gt; {
console.log(‘onResolved4()’, value)
},
(reason) =&gt; {
console.log(‘onRejected2()’, reason)
}
)
// onRejected1() 1</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>在 `catch` 中返回一个新的 `promise`，且这个 `promise` 没有结果。</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>由于，返回的新的 `promise` 结果决定了后面 `then` 中的结果，所以后面的 `then` 中也没有结果。</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>这就实现了中断 `promise链`的效果。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-ji-ben-zhi-ling/</id>
      <title type="text">Vue 基本指令</title>
      <published>2022-09-18T08:23:42.000Z</published>
      <updated>2022-09-18T08:23:42.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-ji-ben-zhi-ling/"/>
      <summary type="text">v text ，该指令的用法同原生 JS 中的 innerText ，更新绑定元素内部的文本内容 {{msg}} 与插值语法的区别： v text 会替换掉节点中的内容， {{xx}} 则不会。</summary>
      <content type="html"><![CDATA[<section><h2>v-text<a href="#v-text"><span>#</span></a></h2><p><code>v-text</code>，该指令的用法同原生 JS 中的 <code>innerText</code>，更新绑定元素内部的文本内容</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;div id="div"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>&lt;span v-text="msg"&gt;&lt;/span&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;!--不会有闪烁问题--&gt;&lt;!-- 和下面的一样 --&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;span&gt;{{msg}}&lt;/span&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>与插值语法的区别：<code>v-text</code>会替换掉节点中的内容，<code>{{xx}}</code>则不会。</p></section>
<section><h2>v-html<a href="#v-html"><span>#</span></a></h2><p><code>v-html</code>，该指令用法同原生 JS 的 <code>innerHTML</code>，可以解析内部的 HTML 标签</p></section>
<section><h2>v-cloak<a href="#v-cloak"><span>#</span></a></h2><p><code>v-cloak</code>，该指令 CSS 一起使用，用于隐藏还没有开始编译的<code>{{}}</code>(插值表达式)直到编译完成，解决其闪烁问题
使用<code>css</code>配合<code>v-cloak</code>可以解决网速慢时页面展示出<code>{{xxx}}</code>的问题。</p><p>本质是一个特殊属性，Vue实例创建完毕并接管容器后，会删掉<code>v-cloak</code>属性。</p></section>
<section><h2>v-show<a href="#v-show"><span>#</span></a></h2><p><code>v-show</code>，该指令用于显示和隐藏 DOM 元素，<strong>根据表达式之真假值</strong>，切换元素的 <code>display</code> 属性值</p><ul>
<li><code>v-show</code> 值为<code>true</code>时显示</li>
<li>当条件变化时该指令触发过渡效果，切换频率较高的场景</li>
<li>不展示的DOM元素未被移除，仅仅是使用样式隐藏掉</li>
<li><code>v-show</code> 指令不支持在<code>&lt;template&gt;&lt; /template&gt;</code>标签上写，也不支持 <code>v-else</code>，一般来说，<code>v-if</code> 有更高的切换开销，而 <code>v-show</code> 有更高的初始渲染开销。因此，如果需要非常频繁地切换，则使用 <code>v-show</code> 较好，如果在运行时条件很少改变，则使用 <code>v-if</code> 较好</li>
</ul></section>
<section><h2>v-if，v-else 与 v-else-if<a href="#v-ifv-else-与-v-else-if"><span>#</span></a></h2><p><code>v-if</code>，该指令用于是否渲染 DOM 元素，传入一个布尔值，通过该布尔值的条件改变实现对于 DOM 元素的删除和重建，该指令不同于 <code>v-show</code>，而是会完全销毁这个 DOM 元素
**注意:**当条件变化时该指令触发过渡效果</p><p><code>v-else</code>， 该指令用于 不需要任何表达式 ，但是在该指令的前一个兄弟元素必须有 <code>v-if</code> 或者 <code>v-else-if</code> 指令，该指令的用处是为 <code>v-if</code> 或者 <code>v-else-if</code> 添加 <code>else</code> 的选项</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;div v-if="Math.random() &gt; 0.5"&gt;Now you see me&lt;/div&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;div v-else&gt;Now you don't&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>v-else-if</code>，该指令用于用于 <code>v-if</code> 的 <code>else if</code> 选项，前一个兄弟元素必须要 <code>v-if</code> 指令，并且该指令可以链式调用，也就是说可以调用多次 <code>v-else-if</code> 指令</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;div v-if="type === 'A'"&gt;A&lt;/div&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;div v-else-if="type === 'B'"&gt;B&lt;/div&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;div v-else-if="type === 'C'"&gt;C&lt;/div&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;div v-else&gt;Not A/B/C&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>v-for<a href="#v-for"><span>#</span></a></h2><p><code>v-for</code>该指令可以基于源数据多次渲染元素或模板块，用于循环遍历某个数组或对象，如果只用一个变量代表得到是数组或对象的 <code>value</code> 值</p><p>**注意&lt;在内部必须使用固定的&gt; <code>item in/of items</code>**形式的特殊语法(下面的 <code>in</code> 都可以用 <code>of</code> 代替，并且更符合实际)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;ul id="example-1"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>&lt;li v-for="item in items"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>{{ item.message }}</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>&lt;!--Foo Bar--&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;/li&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;script&gt;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>var example1 = new Vue({</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>el: '#example-1'，</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>data: {</span></div></div><div><div><div>11</div></div><div><span><span>                </span></span><span>items: [{ message: 'Foo' }， { message: 'Bar' }]，</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>}，</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>遍历数组</strong>时其实还支持第二个变量，代表的是遍历数组的索引值</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;ul id="example-2"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>&lt;li v-for="(item， index) in items"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>{{ parentMessage }} - {{ index }} - {{ item.message }}</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>&lt;!--</span></div></div><div><div><div>5</div></div><div><span><span>              </span></span><span>Parent-0-Foo</span></div></div><div><div><div>6</div></div><div><span><span>              </span></span><span>Parent-1-Bar</span></div></div><div><div><div>7</div></div><div><span><span>          </span></span><span>--&gt;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>&lt;/li&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>&lt;script&gt;</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>var example2 = new Vue({</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>el: '#example-2'，</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>data: {</span></div></div><div><div><div>14</div></div><div><span><span>                </span></span><span>parentMessage: 'Parent'，</span></div></div><div><div><div>15</div></div><div><span><span>                </span></span><span>items: [{ message: 'Foo' }， { message: 'Bar' }]，</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>}，</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ul>
<li>
<p><code>遍历对象</code>时还可以接受第二三两个变量，分别代表了键名和索引值</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;ul id="v-for-object" class="demo"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>&lt;div v-for="(value， key， index) in object"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>{{ index }}. {{ key }}: {{ value }}</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>&lt;!--</span></div></div><div><div><div>5</div></div><div><span><span>         </span></span><span>0. firstName: John</span></div></div><div><div><div>6</div></div><div><span><span>         </span></span><span>1. lastName: Doe</span></div></div><div><div><div>7</div></div><div><span><span>         </span></span><span>2. age: 30</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>--&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>&lt;script&gt;</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>new Vue({</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>el: '#v-for-object'，</span></div></div><div><div><div>14</div></div><div><span><span>          </span></span><span>data: {</span></div></div><div><div><div>15</div></div><div><span><span>              </span></span><span>object: {</span></div></div><div><div><div>16</div></div><div><span><span>                  </span></span><span>firstName: 'John'，</span></div></div><div><div><div>17</div></div><div><span><span>                  </span></span><span>lastName: 'Doe'，</span></div></div><div><div><div>18</div></div><div><span><span>                  </span></span><span>age: 30，</span></div></div><div><div><div>19</div></div><div><span><span>              </span></span><span>}，</span></div></div><div><div><div>20</div></div><div><span><span>          </span></span><span>}，</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul><p>**注意:**当 <code>v-for</code> 和 <code>v-if</code> 处于同一节点时，<code>v-for</code> 的优先级比 <code>v-if</code> 更高，这意味着 <code>v-if</code> 将分别重复运行于每个 <code>v-for</code> 循环中，当想为仅有的一些项渲染节点时，这种优先级的机制会十分有用</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;li v-for="todo in todos" v-if="!todo.isComplete"&gt;{{ todo }}&lt;/li&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;!--</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>官方并不建议v-for和v-if指令一起使用，如果要使用这种方式，就进行条件渲染</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>--&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;!--</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>如果想有条件才继续v-for指令，则可以将v-if置于外层元素(或&lt;template&gt;)上</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>--&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;ul v-if="todos.length"&gt;</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>&lt;li v-for="todo in todos"&gt;{{ todo }}&lt;/li&gt;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;p v-else&gt;No todos left!&lt;/p&gt;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>&lt;!--</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>条件渲染</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>v-if如果想只写一个而同时切换多个元素，可以在最外成包裹一层&lt;template&gt;&lt;/template&gt;，</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>在这个标签上面使用v-if，在渲染的时候并不会将&lt;template&gt;标签渲染出来</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>--&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>v-bind<a href="#v-bind"><span>#</span></a></h2><p><code>v-bind</code>，该指令用于动态绑定一个或多个特性，该方法绑定的特性与普通的属性效果一致，只是其内部会被解析为 JS 表达式，而不是普通特性一样会是一个字符串
<strong>修辞符</strong></p><ul>
<li><code>.prop</code>  被用于绑定 DOM 属性(<code>property</code>)</li>
<li><code>.camel</code>  将短横线命名法特性名转换为驼峰命名法</li>
<li><code>.sync</code>  会扩展成一个更新父组件绑定值的 <code>v-on</code> 侦听器</li>
</ul><p><strong>注意</strong></p><ul>
<li>
<p>该指令有其简写写法，通过冒号<code>:</code>来代替 <code>v-bind</code>:</p>
</li>
<li>
<p>该指令可以动态对绑定的参数进行改变，参数用<code>[]</code>括起来</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;!-- 绑定一个属性 --&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;img v-bind:src="imageSrc"&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;!-- 动态特性名 (2.6.0+) --&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;button v-bind:[key]="value"&gt;&lt;/button&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;!-- 缩写 --&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;img :src="imageSrc"&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;!-- 动态特性名缩写 (2.6.0+) --&gt;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>&lt;button :[key]="value"&gt;&lt;/button&gt;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>&lt;!-- 内联字符串拼接 --&gt;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>&lt;img :src="'/path/to/images/' + fileName"&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>&lt;!-- class 绑定 --&gt;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>&lt;div :class="{ red: isRed }"&gt;&lt;/div&gt;&lt;!--对象通过布尔值确定是否传入class中--&gt;</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>&lt;div :class="[classA， classB]"&gt;&lt;/div&gt;&lt;!--数组会直接将类名传入到class中--&gt;</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>&lt;div :class="[classA， { classB: isB， classC: isC }]"&gt;&lt;!--对象和数组可以混用--&gt;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>&lt;!-- style 绑定 --&gt;</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>&lt;div :style="{ fontSize: size + 'px' }"&gt;&lt;/div&gt;&lt;!--通过对象写入每个属性--&gt;</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>&lt;div :style="[styleObjectA， styleObjectB]"&gt;&lt;/div&gt;&lt;!--数组的成员中实际是一个个对象--&gt;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>&lt;!-- 绑定一个有属性的对象 --&gt;</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>&lt;div v-bind="{ id: someProp， 'other-attr': otherProp }"&gt;&lt;/div&gt;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>&lt;!-- 通过 prop 修饰符绑定 DOM 属性 --&gt;</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>&lt;div v-bind:text-content.prop="text"&gt;&lt;/div&gt;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>&lt;!-- prop 绑定。“prop”必须在 my-component 中声明。--&gt;</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>&lt;my-component :prop="someThing"&gt;&lt;/my-component&gt;</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>&lt;!-- 通过 $props 将父组件的 props 一起传给子组件 --&gt;</span></div></div><div><div><div>35</div></div><div><span><span>  </span></span><span>&lt;child-component v-bind="$props"&gt;&lt;/child-component&gt;</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>&lt;!-- XLink --&gt;</span></div></div><div><div><div>38</div></div><div><span><span>  </span></span><span>&lt;svg&gt;&lt;a :xlink:special="foo"&gt;&lt;/a&gt;&lt;/svg&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section>
<section><h2>v-on<a href="#v-on"><span>#</span></a></h2><p><code>v-on</code>，该指令用于绑定监听事件，表达式可以是一个方法的名字或一个内联语句(也就是说在传入事件的时候可以选择加()可以选择不加()，推荐在传入参数的时候添加，在不传参的时候写事件名)</p><p>**参数:**该指令的参数为原生 JS 中的事件名，只不过没有 <code>on</code></p><p><strong>修饰符</strong></p><ul>
<li>
<p><code>.stop</code>  调用 <code>event.stopPropagation()</code>，会阻止本元素上的事件进行冒泡传播</p>
</li>
<li>
<p><code>.prevent</code>  调用 <code>event.preventDefault()</code>，不能和<code>.passive</code> 一起使用</p>
</li>
<li>
<p><code>.capture</code>  添加事件侦听器时使用 <code>capture</code> 模式，父元素会执行在子元素上进行的同名事件</p>
</li>
<li>
<p><code>.self</code>  只当事件是从侦听器绑定的元素本身触发时才触发回调，只会真正自己触发的事件才会进行，和<code>.stop</code> 是有区别的，这个只会阻止自己的冒泡，但不会阻止该元素的父元素事件的冒泡进行</p>
</li>
<li>
<p><code>.{keyCode | keyAlias}</code>  只当事件是从特定键触发时才触发回调，可以是表示键盘字符的数字或者表示特效事件的按键修饰符</p>
</li>
<li>
<p><code>.native</code>  监听组件根元素的原生事件，一个 Vue 实例内部通过只能 v-on 只能绑定自己内部的方法，不能绑定原生 DOM 事件的方法，通过该修饰符就可以使用原生 JS 的事件方法了</p>
</li>
<li>
<p><code>.once</code>  只触发一次回调</p>
</li>
<li>
<p><code>.left</code>  只当点击鼠标左键时触发</p>
</li>
<li>
<p><code>.right</code>  只当点击鼠标右键时触发</p>
</li>
<li>
<p><code>.middle</code>  只当点击鼠标中键时触发</p>
</li>
<li>
<p><code>.passive</code>  不用查找是否阻止默认事件的请求直接进行操作。
如在滚动页面时的 <code>onscroll</code> 事件，每次触发事件时浏览器都会查看是否有阻止默认滚动事件的操作，但是如果本来就没有进行这个操作，在滚动的时候就会出现卡的的情况，因为在触发滚动条滚动时总会先查找请求，这个修饰符就是告诉浏览器不用进行查找直接进行滚动操作。因为作用的冲突，所以不能和<code>.prevent</code> 一起使用</p>
</li>
</ul><p><strong>注意:</strong></p><ul>
<li>
<p>该指令有简写形式，通过<code>@</code>来代替 <code>v-on</code>:</p>
</li>
<li>
<p>该指令可以动态对绑定的参数进行改变，参数用中括号<code>[]</code>括起来</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;!-- 方法处理器 --&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;button v-on:click="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;!-- 动态事件 (2.6.0+) --&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;button v-on:[event]="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;!-- 内联语句 --&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;button v-on:click="doThat('hello'， $event)"&gt;&lt;/button&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;!-- 缩写 --&gt;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>&lt;button @click="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>&lt;!-- 动态事件缩写 (2.6.0+) --&gt;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>&lt;button @[event]="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>&lt;!-- 停止冒泡 --&gt;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>&lt;button @click.stop="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>&lt;!-- 阻止默认行为 --&gt;</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>&lt;button @click.prevent="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>&lt;!-- 阻止默认行为，没有表达式 --&gt;</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>&lt;form @submit.prevent&gt;&lt;/form&gt;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>&lt;!--  串联修饰符 --&gt;</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>&lt;button @click.stop.prevent="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>&lt;!-- 键修饰符，键别名 --&gt;</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>&lt;input @keyup.enter="onEnter"&gt;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>&lt;!-- 键修饰符，键代码 --&gt;</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>&lt;input @keyup.13="onEnter"&gt;</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>&lt;!-- 点击回调只会触发一次 --&gt;</span></div></div><div><div><div>35</div></div><div><span><span>  </span></span><span>&lt;button v-on:click.once="doThis"&gt;&lt;/button&gt;</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>&lt;!-- 对象语法 (2.4.0+) --&gt;</span></div></div><div><div><div>38</div></div><div><span><span>  </span></span><span>&lt;button v-on="{ mousedown: doThis， mouseup: doThat }"&gt;&lt;/button&gt;</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>&lt;!-- 组件中的自定义事件 --&gt;</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>&lt;my-component @my-event="handleThis"&gt;&lt;/my-component&gt;</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span><span>  </span></span><span>&lt;!-- 内联语句 --&gt;</span></div></div><div><div><div>44</div></div><div><span><span>  </span></span><span>&lt;my-component @my-event="handleThis(123， $event)"&gt;&lt;/my-component&gt;</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span><span>  </span></span><span>&lt;!-- 组件中的原生事件 --&gt;</span></div></div><div><div><div>47</div></div><div><span><span>  </span></span><span>&lt;my-component @click.native="onClick"&gt;&lt;/my-component&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section>
<section><h2>v-model<a href="#v-model"><span>#</span></a></h2><p><code>v-model</code>，该指令用于表单内的标签进行双向的数据绑定</p><p>**注意:**v-model 只能绑定给 <code>input</code>，<code>textarea</code>，<code>select</code> 等表单元素和自定义的组件中，除此之外不能在其他标签上绑定
<strong>与<code>v-bind</code>的区别</strong>：</p><ul>
<li><code>v-bind</code>是单向绑定，而<code>v-model</code>是双向绑定；</li>
<li><code>v-bind</code>只能将<code>vue</code>中的数据同步到页面，而<code>v-model</code>不只能将<code>vue</code>中的数据同步到页面，还可以将用户数据的数据赋值给<code>vue</code>中的属性</li>
</ul><p><strong>修辞符</strong>:</p><ul>
<li><code>.lazy</code>  取代 input 监听 change 事件，默认在用户写入值时是使用的 <code>input</code> 事件，也就是当值输入完成后才会触发事件，而 change 是一些有输入法的语言在值还没有输入时就时刻监听改变</li>
<li><code>.number</code> 输入字符串转为有效的数字</li>
<li><code>.trim</code>  输入首尾空格过滤</li>
</ul><p><strong>用法</strong>:</p><ul>
<li>
<p><strong>如果是对 <code>input</code> 文本框和 <code>textarea</code> 绑定的</strong>，绑定的值会根据输入的内容，就是将内部的变量的值改为 value 中的值</p>
</li>
<li>
<p>**如果是对复选框绑定的 **</p>
<ul>
<li>
<p><strong>如果绑定的变量不是数组</strong>，会根据复选框是否被选中而改变为 false 或 true，即使原来不是布尔值也会被强制转换为布尔值，这是因为双向的数据绑定，如果是有多个复选框，那么则会一起被选中或不选中</p>
</li>
<li>
<p><strong>如果变量是数组</strong>，则会将 value 属性中的值(没有写 value 属性会传入 null)传入该数组作为其中的一个成员，Vue 会根据数组内部的值来判断是否选中该复选框(内部其实就是这样运作的)，如果 value 值一样会有多个复选框被选中，再次点击就会将该值删去然后取消复选框的选中</p>
  <div>
      
      Jack
      
      John
      
      Mike
      <br />
      <span>Checked names: {{ checkedNames }}</span>
  </div>
  
<p></p>
  
</li>
</ul>
</li>
<li>
<p><strong>如果是对单选按钮进行绑定的</strong>，变量值会随着选中单选框的 <code>value</code> 值而变化，如果变量的值刚开始就是一个单选框的 <code>value</code> 值，那么就会自动选中这个单选框
**注意: **</p>
<ul>
<li>
<p>单选框和复选框即使不写相同的 <code>name</code> 而只绑定了相同的 <code>modle</code> 也会认做是一类单选框的，但是最好还是将 <code>name</code> 写上</p>
</li>
<li>
<p>单选框和复选框都可以通过 <code>v-bind:value</code> 绑定的 <code>value</code> 值来设置值自身的 <code>value</code> 值</p>
</li>
</ul>
</li>
<li>
<p>如果是对选择框 <code>select</code> 进行绑定的 ，绑定的变量的值会随着选中的 option 选项内部的内容而发生变化，如果 <code>option</code> 没有写 <code>value</code> 属性，该变量会变成<code>&lt;option&gt;&lt;/option&gt;</code>内部的值，如果有 <code>value</code> 属性，变量会变成 <code>value</code> 值而不是 <code>option</code> 的内容</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;div id="example"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>&lt;select v-model="selected"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>&lt;option disabled value=""&gt;请选择&lt;/option&gt;</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>&lt;option&gt;A&lt;/option&gt;</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>&lt;option&gt;B&lt;/option&gt;</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>&lt;option&gt;C&lt;/option&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;/select&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;span&gt;Selected: {{ selected }}&lt;/span&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;script&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>new Vue({</span></div></div><div><div><div>12</div></div><div><span><span>          </span></span><span>el: '#example'，</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>data: {</span></div></div><div><div><div>14</div></div><div><span><span>              </span></span><span>selected: ''，</span></div></div><div><div><div>15</div></div><div><span><span>          </span></span><span>}，</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul><p><strong>注意:</strong>
在 s 多选时要绑定一个数组，将上方的 <code>selected:[]</code>，这样在选入的时候就能将每个选择的选项的值加到变量中去，还可以是一个对象，但是要通过 <code>v-bind:value</code> 绑定</p></section>
<section><h2>v-pre<a href="#v-pre"><span>#</span></a></h2><p>该指令不接受任何表达式，使用了该指令的元素 Vue 在编译时跳过这个元素和它的子元素的编译过程，可以用来显示原始的模板<code>{{}}</code>标签，跳过大量没有指令的节点会加快编译</p></section>
<section><h2>v-once<a href="#v-once"><span>#</span></a></h2><p>该指令不需要表达式，<code>只渲染元素和组件一次</code>。随后的重新渲染，元素/组件及其所有的子节点将被视为静态内容并跳过。
以后数据的改变不会引起v-once所在结构的更新，这可以用于优化更新性能</p><ul>
<li>跳过其所在节点的编译过程。</li>
<li>可利用它跳过：没有使用指令语法、没有使用插值语法的节点，会加快编译。</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-sheng-ming-zhou-qi/</id>
      <title type="text">Vue 生命周期</title>
      <published>2022-09-18T07:14:08.000Z</published>
      <updated>2022-09-18T07:14:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-sheng-ming-zhou-qi/"/>
      <summary type="text">又名：生命周期回调函数、生命周期函数、生命周期钩子。 是什么：Vue在关键时刻帮我们调用的一些特殊名称的函数。 生命周期函数的名字不可更改，但函数的具体内容是程序员根据需求编写的。 生命周期函数中的this指向是vm 或 组件实例对象。</summary>
      <content type="html"><![CDATA[<section><h2>生命周期<a href="#生命周期"><span>#</span></a></h2><p>又名：生命周期回调函数、生命周期函数、生命周期钩子。</p><p>是什么：Vue在关键时刻帮我们调用的一些特殊名称的函数。</p><p>生命周期函数的名字不可更改，但函数的具体内容是程序员根据需求编写的。</p><p>生命周期函数中的this指向是vm 或 组件实例对象。</p><p></p><figure><img src="/images/vue/vue_Vue%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F_%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.png" alt="vue生命周期" /><figcaption>vue生命周期</figcaption></figure><p></p></section>
<section><h2>Vue 实例创建阶段<a href="#vue-实例创建阶段"><span>#</span></a></h2><ul>
<li>
<p><code>var vm=new Vue()</code>，创建出一个 Vue 实例对象</p>
</li>
<li>
<p><code>beforeCreat</code>，当 Vue 实例被完全创建出来之前，就会执行该函数，这是表示刚初始化了一个空的 Vue 对象，还未进行数据代理</p>
<p>**注意:**在 beforeCreat 生命周期函数执行的时候，data 和 methods 等中的数据还没有初始化，所以在这里面调用这些 data 或 methods 等中的属性和方法会报错</p>
</li>
<li>
<p><code>created</code>，在该函数中，<strong>data 和 methods 等数据已经被初始化好了</strong>，如果要调用 methods 中的方法或者 data 中的数据，最早只能在 created 中进行操作</p>
<p>**注:**如果要发 Ajax 请求尽量在这个阶段发送</p>
</li>
<li>
<p><strong>在这两个生命周期函数之间（create，beforeMount）进行 Vue 的编译模板</strong>，把 Vue 代码中的那些指令进行执行，最终在内存中生成一个编译好的最终模板字符串(虚拟 DOM)，然后把这个字符串渲染为内存中的 DOM，但是此时只是在内存中渲染 DOM，还没有将其挂载到页面中去</p>
</li>
<li>
<p><code>beforeMount</code>，在该函数中模板已经在内存中了，但是还没有把模板渲染到页面中，也就是说<code>{{}}</code>中的内容还没有被解析，页面中的元素还没有真正被替换，只是写了一些模板字符串 将内存中的 DOM 挂载到页面中去</p>
</li>
<li>
<p><code>mounted</code>，表示内存中的 DOM 已经挂载到页面中了，用户已经可以看到渲染好的页面了</p>
<p>注意: mounted 是实例创建期间执行的最后一个生命周期函数，当执行完 mounted 就表示实例已经完全被创建好了 如果要通过某些插件操作页面上的 DOM 节点，最早要在 mounted 中进行</p>
</li>
</ul></section>
<section><h2>Vue 实例运行阶段<a href="#vue-实例运行阶段"><span>#</span></a></h2><p><strong>下面两个生命周期必须要数据发生改变时才会进行，会根据 data 数据的改变触发 0 次或多次</strong></p><ul>
<li>
<p><code>beforeUpdate</code>，这个函数表明 Vue 实例在运行时数据已经被更新，而页面还没有被更新的时间节点，当执行该函数时，页面中显示的数据还是没有更新前的数据，而 data 中的数据是最新的，页面还没有和数据实现同步</p>
</li>
<li>
<p><strong>在这两个函数之间(beforeUpdate， updated)</strong>，会根据 data 中的最新数据，重新渲染出一份最新的内存 DOM 树(虚拟DOM)，当最新的内存 DOM 树被更新之后，会把最新的内存 DOM 树重新渲染到真实的页面中去，完成数据从 data 到页面视图的更新</p>
</li>
<li>
<p><code>updated</code>，当这个函数执行时证明 data 中的数据已经和页面中的数据保存同步更新了</p>
</li>
</ul></section>
<section><h2>Vue 实例销毁阶段<a href="#vue-实例销毁阶段"><span>#</span></a></h2><ul>
<li><code>beforeDestroy</code>， 当执行该函数时，Vue 实例就已经从运行阶段进入到了销毁阶段，实例上的所以属性如 data，methods 等都还是处于可用状态，还没有真正的执行销毁</li>
<li></li>
<li><code>destroyed</code>，当执行到该函数时，Vue 实例已经被完全销毁，此时所有实例中的属性都不可用了</li>
</ul></section>
<section><h2>注意<a href="#注意"><span>#</span></a></h2><ul>
<li>
<p>常用的生命周期钩子：</p>
<ol>
<li><code>mounted</code>: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。</li>
<li><code>beforeDestroy</code>: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。</li>
</ol>
</li>
<li>
<p>关于销毁Vue实例</p>
<ol>
<li>销毁后借助Vue开发者工具看不到任何信息。</li>
<li>销毁后自定义事件会失效，但原生DOM事件依然有效。</li>
<li>一般不会在<code>beforeDestroy</code>操作数据，因为即便操作数据，也不会再触发更新流程了。</li>
</ol>
</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/vue-shi-li-dui-xiang/</id>
      <title type="text">Vue实例对象</title>
      <published>2022-07-30T08:58:08.000Z</published>
      <updated>2022-07-30T08:58:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/vue-shi-li-dui-xiang/"/>
      <summary type="text">Vue 也是一个构造函数,通过 new Vue() 可以创建一个 Vue 对象,通过 Vue 对象进行对 DOM 元素以及内部的子孙元素的操作 new Vue({ el: &apos; div&apos;, / 在Vue中通过el属性来绑定一个DOM元素,从而让该DOM元素以及内部都绑定Vue的相关操作,一般是通过ID…</summary>
      <content type="html"><![CDATA[<section><h2>创建并绑定 Vue 对象<a href="#创建并绑定-vue-对象"><span>#</span></a></h2><p>Vue 也是一个构造函数,通过 <code>new Vue()</code>可以创建一个 Vue 对象,通过 Vue 对象进行对 DOM 元素以及内部的子孙元素的操作</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>new Vue({</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>el: '#div',</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>/*</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>在Vue中通过el属性来绑定一个DOM元素,从而让该DOM元素以及内部都绑定Vue的相关操作,一般是通过ID进行查找,因为这样才能够精确绑定</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>data: {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>//data属性中包含着在el中使用的使用的变量或属性</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>msg: 123,</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>//也可以使用函数形式的</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>data() {</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>return {</span></div></div><div><div><div>13</div></div><div><span><span>                </span></span><span>msg: 123,</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>//methods属性包含着需要使用的方法</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>methods: {</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>show() {</span></div></div><div><div><div>19</div></div><div><span><span>                </span></span><span>console.log(this.msg)</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>注&lt;在实例内部使用定义的属性或方法时不能直接使用&gt;,必须通过 <code>this</code> 来指定需要用的属性</strong></p></section>
<section><h2>vue 实例中的参数与选项<a href="#vue-实例中的参数与选项"><span>#</span></a></h2><ul>
<li>
<p><code>el:"#id"</code>, //DOM 成员（1/3）
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。</p>
</li>
<li>
<p><code>template:"&lt;tag&gt;&lt;/tag&gt;"</code>, //DOM 成员（2/3）
一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略，除非模板的内容有分发</p>
</li>
<li>
<p><code>slot render: (h)=&gt;{h(App)}</code>, //DOM 成员（3/3）
字符串模板的代替方案，允许你发挥 JavaScript 最大的编程能力。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 原理</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>render: (h) =&gt; {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>return &lt;APP&gt;&lt;/APP&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>} // 可以代替components和template</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><code>data</code> //数据成员（1/6）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>data():{ return{ } }</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Vue 实例的数据对象。Vue 将会递归将 <code>data</code> 的属性转换为 <code>getter/setter</code>，从而让 <code>data</code> 的属性能够响应数据变化</p>
</li>
<li>
<p><code>methods</code> //数据成员（2/6）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>methods:{ func(){ } }</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p><code>methods</code> 将被混入到 Vue 实例中，可以直接通过 <code>VM</code> 实例访问这些方法，或者在指令表达式中使用 方法中的 <code>this</code> 自动绑定为 Vue 实例</p>
</li>
<li>
<p><code>watch</code> //数据成员（3/6）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>watch: {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>key: value,</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$route(newValue, oldValue) {//监控路由}</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Vue的监视属性，整个为一个对象，键是需要观察的表达式，值是对应回调函数</p>
</li>
<li>
<p><code>computed</code> //数据成员（4/6）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>computed: {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>getTotalCount() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>const totalCount = 0</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>return totalCount</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>vue 的计算属性，将被混入到 Vue 实例中。所有 <code>getter</code> 和 <code>setter</code> 的 <code>this</code> 上下文自动地绑定为 Vue 实例</p>
</li>
<li>
<p><code>props</code> //数据成员（5/6）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>props:['counts','ids']</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>用于父子组件的 <code>eventbus</code> 传值，是数组或对象，<code>props</code> 的成员是子组件接收的来自父组件的数据</p>
</li>
<li>
<p><code>propsData</code> //数据成员（6/6）
创建实例时传递 <code>props</code>。主要作用是方便测试。基本不使用。</p>
</li>
<li>
<p><code>filters</code> //资源（1/3）</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>filters(</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>'filterName',</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>(input,</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>function () {</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>return newvalue</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>包含 Vue 实例可用过滤器的哈希表。</p>
</li>
<li>
<p><code>directives</code> //资源（2/3）
包含 Vue 实例可用指令的哈希表。</p>
</li>
<li>
<p><code>components</code> //资源（3/3）
（即该组件的子实例）这里是包含 Vue 实例可用组件的哈希表。</p>
</li>
<li>
<p><code>name</code> //杂项（1/6）
允许组件模板递归地调用自身。注意，组件在全局用 <code>Vue.component()</code> 注册时，全局 ID 自动作为组件的 <code>name</code>。</p>
</li>
<li>
<p><code>parent</code> //杂项（2/6）
指定已创建的实例之父实例，在两者之间建立父子关系。子实例可以用 <code>this.$parent</code> 访问父实例，子实例被推入父实例的 <code>$children</code> 数组中。</p>
</li>
<li>
<p><code>mixins</code> //杂项（3/6）
<code>mixins</code> 选项接受一个混合对象的数组。Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。</p>
</li>
<li>
<p><code>extends</code> //杂项（4/6）
允许声明扩展另一个组件。这主要是为了便于扩展单文件组件。这和 <code>mixins</code> 类似，区别在于，组件自身的选项会比要扩展的源组件具有更高的优先级。</p>
</li>
<li>
<p><code>delimiters</code> //杂项（5/6）
改变纯文本插入分隔符。</p>
</li>
<li>
<p><code>functional</code> //杂项（6/6）
使组件无状态（没有 <code>data</code> ）和无实例（没有 <code>this</code> 上下</p>
</li>
</ul></section>
<section><h2>常用指令实例<a href="#常用指令实例"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;!DOCTYPE html&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;html lang="en"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>&lt;head&gt;</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>&lt;meta charset="UTF-8" /&gt;</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>&lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>&lt;title&gt;startVue&lt;/title&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;/head&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;body&gt;</span></div></div><div><div><div>9</div></div><div><span><span>          </span></span><span>&lt;div id="app"&gt;</span></div></div><div><div><div>10</div></div><div><span><span>              </span></span><span>&lt;!-- {{}}双大括号表达式，里面是js代码 --&gt;</span></div></div><div><div><div>11</div></div><div><span><span>              </span></span><span>&lt;p&gt;{{ msg }}&lt;/p&gt;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>              </span></span><span>&lt;!-- 相当于innerText --&gt;</span></div></div><div><div><div>14</div></div><div><span><span>              </span></span><span>&lt;p v-text="str"&gt;&lt;/p&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>              </span></span><span>&lt;!-- 相当于innerHTML --&gt;</span></div></div><div><div><div>17</div></div><div><span><span>              </span></span><span>&lt;p v-html="str"&gt;&lt;/p&gt;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>              </span></span><span>&lt;!-- 强制绑定事件, 一般用于用于响应式地更新HTML attribute, 简写方式:</span></div></div><div><div><div>20</div></div><div><span><span>              </span></span><span>从 2.6.0 开始，可以用方括号括起来的 JavaScript 表达式作为一个指令的参数</span></div></div><div><div><div>21</div></div><div><span><span>              </span></span><span>&lt;a v-bind:[attributeName]="url"&gt; ... &lt;/a&gt;</span></div></div><div><div><div>22</div></div><div><span><span>              </span></span><span>这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。</span></div></div><div><div><div>23</div></div><div><span><span>              </span></span><span>例如，如果你的 Vue 实例有一个 data 属性 attributeName，其值为 "href"</span></div></div><div><div><div>24</div></div><div><span><span>              </span></span><span>那么这个绑定将等价于 v-bind:href--&gt;</span></div></div><div><div><div>25</div></div><div><span><span>              </span></span><span>&lt;a v-bind:href="url"&gt;Baidu&lt;/a&gt;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>              </span></span><span>&lt;!-- 绑定事件监听,监听 DOM 事件, 简写方式@ --&gt;</span></div></div><div><div><div>28</div></div><div><span><span>              </span></span><span>&lt;button v-on:click="test"&gt;Submit&lt;/button&gt;</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span><span>              </span></span><span>&lt;!-- 双向绑定事件,动态实时更新里面的内容 --&gt;</span></div></div><div><div><div>31</div></div><div><span><span>              </span></span><span>&lt;div&gt;</span></div></div><div><div><div>32</div></div><div><span><span>                  </span></span><span>&lt;input type="text" v-model="input" placeholder="Please input" /&gt;</span></div></div><div><div><div>33</div></div><div><span><span>                  </span></span><span>&lt;p&gt;{{ input }}&lt;/p&gt;</span></div></div><div><div><div>34</div></div><div><span><span>              </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>35</div></div><div><span><span>          </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>36</div></div><div><span><span>      </span></span><span>&lt;/body&gt;</span></div></div><div><div><div>37</div></div><div><span><span>      </span></span><span>&lt;script text="javascript" src="./vue.js"&gt;&lt;/script&gt;</span></div></div><div><div><div>38</div></div><div><span><span>      </span></span><span>&lt;script text="javascript"&gt;</span></div></div><div><div><div>39</div></div><div><span><span>          </span></span><span>const app = new Vue({</span></div></div><div><div><div>40</div></div><div><span><span>              </span></span><span>//要挂载的位置</span></div></div><div><div><div>41</div></div><div><span><span>              </span></span><span>//在Vue中通过el属性来绑定一个DOM元素,从而让该DOM元素以及内部都绑定Vue的相关操作</span></div></div><div><div><div>42</div></div><div><span><span>              </span></span><span>//一般是通过ID进行查找,因为这样才能够精确绑定</span></div></div><div><div><div>43</div></div><div><span><span>              </span></span><span>el: '#app', // data属性中包含着在el中使用的使用的变量或属性</span></div></div><div><div><div>44</div></div><div><span><span>              </span></span><span>data: {</span></div></div><div><div><div>45</div></div><div><span><span>                  </span></span><span>msg: 'xhw',</span></div></div><div><div><div>46</div></div><div><span><span>                  </span></span><span>str: '&lt;a href="#"&gt;XHW&lt;/a&gt;',</span></div></div><div><div><div>47</div></div><div><span><span>                  </span></span><span>url: 'https://www.baidu.com',</span></div></div><div><div><div>48</div></div><div><span><span>                  </span></span><span>input: '',</span></div></div><div><div><div>49</div></div><div><span><span>              </span></span><span>}, // methods属性包含着需要使用的方法</span></div></div><div><div><div>50</div></div><div><span><span>              </span></span><span>/**</span></div></div><div><div><div>51</div></div><div><span><span>               </span></span><span>* 也可以写成(以组件形式里必须写成这个)</span></div></div><div><div><div>52</div></div><div><span><span>               </span></span><span>* data() {</span></div></div><div><div><div>53</div></div><div><span><span>               </span></span><span>*     return {</span></div></div><div><div><div>54</div></div><div><span><span>               </span></span><span>*</span></div></div><div><div><div>55</div></div><div><span><span>               </span></span><span>* }</span></div></div><div><div><div>56</div></div><div><span><span>               </span></span><span>* },</span></div></div><div><div><div>57</div></div><div><span><span>               </span></span><span>*/ methods: {</span></div></div><div><div><div>58</div></div><div><span><span>                  </span></span><span>test() {</span></div></div><div><div><div>59</div></div><div><span><span>                      </span></span><span>alert('!!!')</span></div></div><div><div><div>60</div></div><div><span><span>                  </span></span><span>},</span></div></div><div><div><div>61</div></div><div><span><span>              </span></span><span>},</span></div></div><div><div><div>62</div></div><div><span><span>          </span></span><span>})</span></div></div><div><div><div>63</div></div><div><span><span>      </span></span><span>&lt;/script&gt;</span></div></div><div><div><div>64</div></div><div><span><span>  </span></span><span>&lt;/html&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2><code>vm.$mount</code><a href="#vmmount"><span>#</span></a></h2><p>如果 Vue 实例在实例化时没有收到 <code>el</code> 选项，则它处于“未挂载”状态，没有关联的 DOM 元素。可以使用 <code>vm.$mount()</code> 手动地挂载一个未挂载的实例</p><p>**注意:**这个方法返回实例自身,因而可以链式调用其它实例方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var MyComponent = Vue.extend({ template: '&lt;div&gt;Hello!&lt;/div&gt;' }) // 创建并挂载到 #app (会替换 #app)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>new MyComponent().$mount('#app') // 同上</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>new MyComponent({ el: '#app' }) // 或者，在文档之外渲染并且随后挂载 var</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>component = new MyComponent().$mount()</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>document.getElementById('app').appendChild(component.$el)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/zi-fu-chuan-de-kuo-zhan/</id>
      <title type="text">字符串的扩展</title>
      <published>2022-07-27T06:19:08.000Z</published>
      <updated>2022-07-27T06:19:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/zi-fu-chuan-de-kuo-zhan/"/>
      <summary type="text">JavaScript 允许采用 \uxxxx 形式表示一个字符，其中 xxxx 表示字符的 Unicode 码点。 &apos;\u0061&apos; // &quot;a&quot; 但是，这种表示法只限于码点在 \u0000 \uFFFF 之间的字符。超出这个范围的字符，必须用两个双字节的形式表示</summary>
      <content type="html"><![CDATA[<section><h2>字符的 Unicode 表示法<a href="#字符的-unicode-表示法"><span>#</span></a></h2><p>JavaScript 允许采用<code>\uxxxx</code>形式表示一个字符，其中<code>xxxx</code>表示字符的 Unicode 码点。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'\u0061'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// "a"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是，这种表示法只限于码点在<code>\u0000~\uFFFF</code>之间的字符。超出这个范围的字符，必须用两个双字节的形式表示</p><p>ES6 对这一点做出了改进，只要将码点放入大括号，就能正确解读该字符</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'\u{20BB7}'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// "𠮷"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>'\u{41}\u{42}\u{43}'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// "ABC"</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>let hello = 123</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>hello // 123</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>'\u{1F680}' === '\uD83D\uDE80'</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，最后一个例子表明，大括号表示法与四字节的 UTF-16 编码是等价的。</p><p>有了这种表示法之后，JavaScript 共有 6 种方法可以表示一个字符</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'z' === 'z' // true</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>'\172' === 'z' // true</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>'\x7A' === 'z' // true</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>'\u007A' === 'z' // true</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>'\u{7A}' === 'z' // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>codePointAt()<a href="#codepointat"><span>#</span></a></h2><p>JavaScript 内部，字符以 UTF-16 的格式储存，每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于<code>0xFFFF</code>的字符)，JavaScript 会认为它们是两个字符</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var s = '𠮷'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>s.length // 2</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>s.charAt(0) // ''</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>s.charAt(1) // ''</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>s.charCodeAt(0) // 55362</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>s.charCodeAt(1) // 57271</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，汉字“𠮷”(注意，这个字不是“吉祥”的“吉”)的码点是<code>0x20BB7</code>，UTF-16 编码为<code>0xD842 0xDFB7</code>(十进制为<code>55362 57271</code>)，需要4个字节储存。对于这种4个字节的字符，JavaScript 不能正确处理，字符串长度会误判为2，而且<code>charAt</code>方法无法读取整个字符，<code>charCodeAt</code>方法只能分别返回前两个字节和后两个字节的值</p><p>ES6 提供了<code>codePointAt</code>方法，能够正确处理 4 个字节储存的字符，返回一个字符的码点。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s = '𠮷a'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>s.codePointAt(0) // 134071</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>s.codePointAt(1) // 57271</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>s.codePointAt(2) // 97</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>codePointAt</code>方法的参数，是字符在字符串中的位置(从 0 开始)。上面代码中，JavaScript 将“<code>𠮷 a</code>”视为三个字符，<code>codePointAt</code> 方法在第一个字符上，正确地识别了“𠮷”，返回了它的十进制码点 <code>134071</code>(即十六进制的<code>20BB7</code>)。在第二个字符(即“𠮷”的后两个字节)和第三个字符“a”上，<code>codePointAt</code>方法的结果与<code>charCodeAt</code>方法相同。</p><p>总之，<code>codePointAt</code>方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符，它的返回结果与charCodeAt方法相同。</p><p><code>codePointAt</code>方法返回的是码点的十进制值，如果想要十六进制的值，可以使用<code>toString</code>方法转换一下。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s = '𠮷a'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>s.codePointAt(0).toString(16) // "20bb7"</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>s.codePointAt(2).toString(16) // "61"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>您可能注意到了，<code>codePointAt</code>方法的参数，仍然是不正确的。比如，上面代码中，字符a在字符串s的正确位置序号应该是 1，但是必须向<code>codePointAt</code>方法传入 2。解决这个问题的一个办法是使用<code>for...of</code>循环，因为它会正确识别 32 位的 UTF-16 字符</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s = '𠮷a'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for (let ch of s) {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>console.log(ch.codePointAt(0).toString(16))</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 20bb7</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// 6</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>String.fromCodePoint()<a href="#stringfromcodepoint"><span>#</span></a></h2><p>ES5 提供<code>String.fromCharCode</code>方法，用于从码点返回对应字符，但是这个方法不能识别 32 位的 UTF-16 字符(Unicode 编号大于<code>0xFFFF</code>)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.fromCharCode(0x20bb7)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// "ஷ"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>String.fromCharCode</code>不能识别大于<code>0xFFFF</code>的码点，所以<code>0x20BB7</code>就发生了溢出，最高位2被舍弃了，最后返回码点<code>U+0BB7</code>对应的字符，而不是码点<code>U+20BB7</code>对应的字符。</p><p>ES6 提供了<code>String.fromCodePoint</code>方法，可以识别大于<code>0xFFFF</code>的字符，弥补了<code>String.fromCharCode</code>方法的不足。在作用上，正好与<code>codePointAt</code>方法相反。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>String.fromCodePoint(0x20bb7)</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// "𠮷"</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>String.fromCodePoint(0x78, 0x1f680, 0x79) === "x\uD83D\uDE80y"</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，如果<code>String.fromCodePoint</code>方法有多个参数，则它们会被合并成一个字符串返回。</p><p>注意，<code>fromCodePoint</code>方法定义在<code>String</code>对象上，而<code>codePointAt</code>方法定义在字符串的实例对象上</p></section>
<section><h2>字符串的遍历器接口<a href="#字符串的遍历器接口"><span>#</span></a></h2><p>ES6 为字符串添加了遍历器接口，使得字符串可以被<code>for...of</code>循环遍历。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>for (let codePoint of 'foo') {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console.log(codePoint)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// "f"</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// "o"</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// "o"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>除了遍历字符串，这个遍历器最大的优点是可以识别大于<code>0xFFFF</code>的码点，传统的<code>for</code>循环无法识别这样的码点</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let text = String.fromCodePoint(0x20bb7)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>for (let i = 0; i &lt; text.length; i++) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>console.log(text[i])</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// " "</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// " "</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>for (let i of text) {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>console.log(i)</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>// "𠮷"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，字符串<code>text</code>只有一个字符，但是<code>for</code>循环会认为它包含两个字符(都不可打印)，而<code>for...of</code>循环会正确识别出这一个字符</p></section>
<section><h2>includes(), startsWith(), endsWith()<a href="#includes-startswith-endswith"><span>#</span></a></h2><p>传统上，JavaScript 只有<code>indexOf</code>方法，可以用来确定一个字符串是否包含在另一个字符串中。
ES6 又提供了三种新方法。</p><ul>
<li>
<p><code>includes()</code>: 返回布尔值，表示是否找到了参数字符串。</p>
</li>
<li>
<p><code>startsWith()</code>: 返回布尔值，表示参数字符串是否在原字符串的头部。</p>
</li>
<li>
<p><code>endsWith()</code>: 返回布尔值，表示参数字符串是否在原字符串的尾部</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let s = 'Hello world!'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>s.startsWith('Hello') // true</span></div></div><div><div><div>4</div></div><div><span>s.endsWith('!') // true</span></div></div><div><div><div>5</div></div><div><span>s.includes('o') // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>这三个方法都支持第二个参数，表示开始搜索的位置</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s = 'Hello world!'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>s.startsWith('world', 6) // true</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>s.endsWith('Hello', 5) // true</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>s.includes('Hello', 6) // false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码表示，使用第二个参数n时，<code>endsWith</code>的行为与其他两个方法有所不同。它针对前<code>n</code>个字符，而其他两个方法针对从第<code>n</code>个位置直到字符串结束</p></section>
<section><h2>repeat()<a href="#repeat"><span>#</span></a></h2><p><code>repeat</code>方法返回一个新字符串，表示将原字符串重复<code>n</code>次。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'x'.repeat(3) // "xxx"</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>'hello'.repeat(2) // "hellohello"</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>'na'.repeat(0) // ""</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>参数如果是小数，会被取整。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>"na".repeat(2.9) // "nana"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果<code>repeat</code>的参数是负数或者<code>Infinity</code>，会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'na'.repeat(Infinity)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// RangeError</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>'na'.repeat(-1)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// RangeError</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是，如果参数是 0 到-1 之间的小数，则等同于 0，这是因为会先进行取整运算。0 到-1 之间的小数，取整以后等于-0，<code>repeat</code>视同为 0</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>"na".repeat(-0.9) // ""</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>参数NaN等同于 0。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>"na".repeat(NaN) // ""</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果<code>repeat</code>的参数是字符串，则会先转换成数字。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>"na".repeat("na") // ""</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>"na".repeat("3") // "nanana"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>padStart()，padEnd()<a href="#padstartpadend"><span>#</span></a></h2><p>ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度，会在头部或尾部补全。
<code>padStart()</code>用于头部补全，<code>padEnd()</code>用于尾部补全</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>"x".padStart(5, "ab") // 'ababx'</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>"x".padStart(4, "ab") // 'abax'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>"x".padEnd(5, "ab") // 'xabab'</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>"x".padEnd(4, "ab") // 'xaba'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>padStart</code>和<code>padEnd</code>一共接受两个参数，第一个参数用来指定字符串的最小长度，第二个参数是用来补全的字符串。</p><p>如果原字符串的长度，等于或大于指定的最小长度，则返回原字符串</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'xxx'.padStart(2, 'ab') // 'xxx'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>'xxx'.padEnd(2, 'ab') // 'xxx'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果用来补全的字符串与原字符串，两者的长度之和超过了指定的最小长度，则会截去超出位数的补全字符串</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'abc'.padStart(10, '0123456789')</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// '0123456abc'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果省略第二个参数，默认使用空格补全长度</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'x'.padStart(4) // '   x'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>'x'.padEnd(4) // 'x   '</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>padStart</code>的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'1'.padStart(10, '0') // "0000000001"</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>'12'.padStart(10, '0') // "0000000012"</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>'123456'.padStart(10, '0') // "0000123456"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>另一个用途是提示字符串格式。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>matchAll()<a href="#matchall"><span>#</span></a></h2><p><code>matchAll</code>方法返回一个正则表达式在当前字符串的所有匹配</p></section>
<section><h2>模板字符串<a href="#模板字符串"><span>#</span></a></h2><p>传统的 JavaScript 语言，输出模板通常是这样写的(下面使用了 jQuery 的方法)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$('#result').append(</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>'There are &lt;b&gt;' +</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>basket.count +</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>'&lt;/b&gt; ' +</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>'items in your basket, ' +</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>'&lt;em&gt;' +</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>basket.onSale +</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>'&lt;/em&gt; are on sale!'</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面这种写法相当繁琐不方便，ES6 引入了模板字符串解决这个问题。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$('#result').append(`</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>There are &lt;b&gt;${basket.count}&lt;/b&gt; items</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>in your basket, &lt;em&gt;${basket.onSale}&lt;/em&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>are on sale!</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>`)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>模板字符串(template string)是增强版的字符串，用反引号(```)标识。它可以当作普通字符串使用，也可以用来定义多行字符串，或者在字符串中嵌入变量</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 普通字符串</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>;`In JavaScript '\n' is a line-feed.` // 多行字符串</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>`In JavaScript this is not legal.`</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log(`string text line 1 string text line 2`)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// 字符串中嵌入变量</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>let name = 'Bob',</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>time = 'today'</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>;`Hello ${name}, how are you ${time}?`</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// Hello Bob, how are you today?</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中的模板字符串，都是用反引号表示。如果在模板字符串中需要使用反引号，则前面要用反斜杠转义。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let greeting = `\`Yo\` World!`</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果使用模板字符串表示多行字符串，所有的空格和缩进都会被保留在输出之中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$("#list").html(`</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;ul&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;li&gt;first&lt;/li&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;li&gt;second&lt;/li&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>`)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，所有模板字符串的空格和换行，都是被保留的，比如<code>&lt;ul&gt;</code>标签前面会有一个换行。如果您不想要这个换行，可以使用<code>trim</code>方法消除它</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$("#list").html(</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>`</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;ul&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;li&gt;first&lt;/li&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;li&gt;second&lt;/li&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>`.trim()</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>模板字符串中嵌入变量，需要将变量名写在<code>${}</code>之中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function authorize(user, action) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>if (!user.hasPrivilege(action)) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>throw new Error(</span></div></div><div><div><div>4</div></div><div><span><span>                </span></span><span>// 传统写法为</span></div></div><div><div><div>5</div></div><div><span><span>                </span></span><span>// 'User '</span></div></div><div><div><div>6</div></div><div><span><span>                </span></span><span>// + user.name</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>// + ' is not authorized to do '</span></div></div><div><div><div>8</div></div><div><span><span>                </span></span><span>// + action</span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>// + '.'</span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>`User ${user.name} is not authorized to do ${action}.`</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>)</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>大括号内部可以放入任意的 JavaScript 表达式，可以进行运算，以及引用对象属性。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let x = 1</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let y = 2</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>;`${x} + ${y} = ${x + y}` // "1 + 2 = 3"</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>`${x} + ${y * 2} = ${x + y * 2}`</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// "1 + 4 = 5"</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>let obj = { x: 1, y: 2 }</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>;`${obj.x + obj.y}`</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// "3"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>模板字符串之中还能调用函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function fn() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return 'Hello World'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>;`foo ${fn()} bar`</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// foo Hello World bar</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果大括号中的值不是字符串，将按照一般的规则转为字符串。比如，大括号中是一个对象，将默认调用对象的<code>toString</code>方法</p><p>如果模板字符串中的变量没有声明，将报错。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 变量place没有声明</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let msg = `Hello, ${place}`</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>// 报错</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>由于模板字符串的大括号内部，就是执行 JavaScript 代码，因此如果大括号内部是一个字符串，将会原样输出</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>`Hello ${"World"}`</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// "Hello World"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>标签模板<a href="#标签模板"><span>#</span></a></h2><p>模板字符串的功能，不仅仅是上面这些。它可以紧跟在一个函数名后面，该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>alert`123`</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>alert(123)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>标签模板其实不是模板，而是函数调用的一种特殊形式。“标签”指的就是函数，紧跟在后面的模板字符串就是它的参数。</p><p>但是，如果模板字符里面有变量，就不是简单的调用了，而是会将模板字符串先处理成多个参数，再调用函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let a = 5</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let b = 10</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>tag`Hello ${a + b} world ${a * b}`</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>tag(['Hello ', ' world ', ''], 15, 50)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，模板字符串前面有一个标识名tag，它是一个函数。整个表达式的返回值，就是tag函数处理模板字符串后的返回值。</p><p>函数<code>tag</code>依次会接收到多个参数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function tag(stringArr, value1, value2) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>function tag(stringArr, ...values) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>tag</code>函数的第一个参数是一个数组，该数组的成员是模板字符串中那些没有变量替换的部分，也就是说，变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间，以此类推。</p><p><code>tag</code>函数的其他参数，都是模板字符串各个变量被替换后的值。由于本例中，模板字符串含有两个变量，因此<code>tag</code>会接受到<code>value1</code>和<code>value2</code>两个参数。</p><p><code>tag</code>函数的第一个参数是一个数组，该数组的成员是模板字符串中那些没有变量替换的部分，也就是说，变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间，以此类推。</p><p><code>tag</code>函数的其他参数，都是模板字符串各个变量被替换后的值。由于本例中，模板字符串含有两个变量，因此<code>tag</code>会接受到<code>value1</code>和<code>value2</code>两个参数。</p><p>tag函数所有参数的实际值如下。</p><ul>
<li>第一个参数: [‘Hello ’, ’ world ’, ”]</li>
<li>第二个参数: 15</li>
<li>第三个参数: 50</li>
</ul><p>也就是说，tag函数实际上以下面的形式调用。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>tag(["Hello ", " world ", ""], 15, 50)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们可以按照需要编写<code>tag</code>函数的代码。下面是<code>tag</code>函数的一种写法，以及运行结果。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let a = 5</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let b = 10</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>function tag(s, v1, v2) {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>console.log(s[0])</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>console.log(s[1])</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>console.log(s[2])</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>console.log(v1)</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console.log(v2)</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>return 'OK'</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>tag`Hello ${a + b} world ${a * b}`</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>// "Hello "</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>// " world "</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>// ""</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>// 15</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>// 50</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>// "OK"</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>“标签模板”的一个重要应用，就是过滤 HTML 字符串，防止用户输入恶意内容。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let message = SaferHTML`&lt;p&gt;${sender} has sent you a message.&lt;/p&gt;`</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>function SaferHTML(templateData) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>let s = templateData[0]</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>for (let i = 1; i &lt; arguments.length; i++) {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>let arg = String(arguments[i])</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>// Escape special characters in the substitution.</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>s += arg</span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>.replace(/&amp;/g, '&amp;amp;')</span></div></div><div><div><div>11</div></div><div><span><span>                </span></span><span>.replace(/&lt;/g, '&amp;lt;')</span></div></div><div><div><div>12</div></div><div><span><span>                </span></span><span>.replace(/&gt;/g, '&amp;gt;')</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>// Don't escape special characters in the template.</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>s += templateData[i]</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>return s</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>sender</code>变量往往是用户提供的，经过<code>SaferHTML</code>函数处理，里面的特殊字符都会被转义。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let sender = '&lt;script&gt;alert("abc")&lt;/script&gt;' // 恶意代码</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let message = SaferHTML`&lt;p&gt;${sender} has sent you a message.&lt;/p&gt;`</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>message</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>// &lt;p&gt;&amp;lt;script&amp;gt;alert("abc")&amp;lt;/script&amp;gt; has sent you a message.&lt;/p&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>String.raw()<a href="#stringraw"><span>#</span></a></h2><p>ES6 还为原生的 String 对象，提供了一个<code>raw</code>方法。</p><p><code>String.raw</code>方法，往往用来充当模板字符串的处理函数，返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串，对应于替换变量后的模板字符串</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.raw`Hi\n${2 + 3}!`</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 返回 "Hi\\n5!"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>String.raw`Hi\u000A!`</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 返回 "Hi\\u000A!"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果原字符串的斜杠已经转义，那么<code>String.raw</code>会进行再次转义。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>String.raw`Hi\\n`</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// 返回 "Hi\\\\n"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>String.raw</code>方法可以作为处理模板字符串的基本方法，它会将所有变量替换，而且对斜杠进行转义，方便下一步作为字符串来使用。</p><p><code>String.raw</code>方法也可以作为正常的函数使用。这时，它的第一个参数，应该是一个具有<code>raw</code>属性的对象，且<code>raw</code>属性的值应该是一个数组。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.raw({ raw: 'test' }, 0, 1, 2)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 't0e1s2t'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>String.raw({ raw: ['t', 'e', 's', 't'] }, 0, 1, 2)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>作为函数，<code>String.raw</code>的代码实现基本如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.raw = function (strings, ...values) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>let output = ''</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>let index</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>for (index = 0; index &lt; values.length; index++) {</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>output += strings.raw[index] + values[index]</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>output += strings.raw[index]</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>return output</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>模板字符串的限制<a href="#模板字符串的限制"><span>#</span></a></h2><p>前面提到标签模板里面，可以内嵌其他语言。但是，模板字符串默认会将字符串转义，导致无法嵌入其他语言。</p><p>举例来说，标签模板里面可以嵌入 LaTEX 语言。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function latex(strings) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>let document = latex`</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>\newcommand{\fun}{\textbf{Fun!}}  // 正常工作</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>\newcommand{\unicode}{\textbf{Unicode!}} // 报错</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>\newcommand{\xerxes}{\textbf{King!}} // 报错</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>Breve over the h goes \u{h}ere // 报错</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>`</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，变量<code>document</code>内嵌的模板字符串，对于 LaTEX 语言来说完全是合法的，但是 JavaScript 引擎会报错。原因就在于字符串的转义。</p><p>模板字符串会将<code>\u00FF</code>和<code>\u{42}</code>当作 Unicode 字符进行转义，所以解析<code>\unicode</code>时报错；而<code>\x56</code>会被当作十六进制字符串转义，所以<code>\xerxes</code>会报错。
也就是说，<code>\u</code>和<code>\x</code>在 LaTEX 里面有特殊含义，但是 JavaScript 将它们转义了。</p><p>为了解决这个问题，ES2018 放松了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义，就返回<code>undefined</code>，而不是报错，并且从<code>raw</code>属性上面可以得到原始字符串。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function tag(strs) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>strs[0] === undefined</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>strs.raw[0] === '\\unicode and \\u{55}'</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>tag`\unicode and \u{55}`</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，模板字符串原本是应该报错的，但是由于放松了对字符串转义的限制，所以不报错了，JavaScript 引擎将第一个字符设置为<code>undefined</code>，但是raw属性依然可以得到原始字符串，因此<code>tag</code>函数还是可以对原字符串进行处理。</p><p>注意，这种对字符串转义的放松，只在标签模板解析字符串时生效，不是标签模板的场合，依然会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let bad = `bad escape sequence: \unicode` // 报错</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/module-de-jia-zai-shi-xian/</id>
      <title type="text">Module 的加载实现</title>
      <published>2022-07-24T00:58:59.000Z</published>
      <updated>2022-07-24T00:58:59.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/module-de-jia-zai-shi-xian/"/>
      <summary type="text">本章介绍如何在浏览器和 Node 之中加载 ES6 模块，以及实际开发中经常遇到的一些问题(比如循环加载) 在 HTML 网页中，浏览器通过 标签加载 JavaScript 脚本。</summary>
      <content type="html"><![CDATA[<p>本章介绍如何在浏览器和 Node 之中加载 ES6 模块，以及实际开发中经常遇到的一些问题(比如循环加载)</p>
<section><h2>浏览器加载<a href="#浏览器加载"><span>#</span></a></h2><section><h3>传统方法<a href="#传统方法"><span>#</span></a></h3><p>在 HTML 网页中，浏览器通过<code>&lt;script&gt;</code>标签加载 JavaScript 脚本。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;!-- 页面内嵌的脚本 --&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;script type="application/javascript"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>// module code</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;/script&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;!-- 外部脚本 --&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;script type="application/javascript" src="path/to/myModule.js"&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，由于浏览器脚本的默认语言是 JavaScript，因此<code>type="application/javascript"</code>可以省略</p><p>默认情况下，浏览器是同步加载 JavaScript 脚本，即渲染引擎遇到<code>&lt;script&gt;</code>标签就会停下来，等到执行完脚本，再继续向下渲染。如果是外部脚本，还必须加入脚本下载的时间</p><p>如果脚本体积很大，下载和执行的时间就会很长，因此造成浏览器堵塞，用户会感觉到浏览器“卡死”了，没有任何响应。这显然是很不好的体验，所以浏览器允许脚本<code>异步加载</code>，下面就是两种异步加载的语法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script src="path/to/myModule.js" defer&gt;&lt;/script&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;script src="path/to/myModule.js" async&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>&lt;script&gt;</code>标签打开<code>defer</code>或<code>async</code>属性，脚本就会异步加载。渲染引擎遇到这一行命令，就会开始下载外部脚本，但不会等它下载和执行，而是直接执行后面的命令</p><p>注：
<code>defer</code>与<code>async</code>的区别是: <code>defer</code>要等到整个页面在内存中正常渲染结束(DOM 结构完全生成，以及其他脚本执行完成)，才会执行；<code>async</code>一旦下载完，渲染引擎就会中断渲染，执行这个脚本以后，再继续渲染。一句话，<code>defer</code>是“渲染完再执行”，<code>async</code>是“下载完就执行”。另外，如果有多个<code>defer</code>脚本，会按照它们在页面出现的顺序加载，而多个<code>async</code>脚本是不能保证加载顺序的</p></section><section><h3>加载规则<a href="#加载规则"><span>#</span></a></h3><p>浏览器加载 ES6 模块，也使用<code>&lt;script&gt;</code>标签，但是要加入<code>type="module"</code>属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script type="module" src="./foo.js"&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码在网页中插入一个模块<code>foo.js</code>，由于<code>type</code>属性设为<code>module</code>，所以浏览器知道这是一个 ES6 模块</p><p>浏览器对于带有<code>type="module"</code>的<code>&lt;script&gt;</code>，都是异步加载，不会造成堵塞浏览器，即等到整个页面渲染完，再执行模块脚本，等同于打开了<code>&lt;script&gt;</code>标签的<code>defer</code>属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script type="module" src="./foo.js"&gt;&lt;/script&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;!-- 等同于 --&gt;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;script type="module" src="./foo.js" defer&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果网页有多个<code>&lt;script type="module"&gt;</code>，它们会按照在页面出现的顺序依次执行</p><p><code>&lt;script&gt;</code>标签的<code>async</code>属性也可以打开，这时只要加载完成，渲染引擎就会中断渲染立即执行。执行完成后，再恢复渲染</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script type="module" src="./foo.js" async&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>一旦使用了<code>async</code>属性，<code>&lt;script type="module"&gt;</code>就不会按照在页面出现的顺序执行，而是只要该模块加载完成，就执行该模块</p><p>ES6 模块也允许内嵌在网页中，语法行为与加载外部脚本完全一致</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;script type="module"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>import utils from './utils.js'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>// other code</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对于外部的模块脚本(上例是<code>foo.js</code>)，有几点需要注意</p><ul>
<li>代码是在模块作用域之中运行，而不是在全局作用域运行。模块内部的顶层变量，外部不可见。</li>
<li>模块脚本自动采用严格模式，不管有没有声明<code>use strict</code>。</li>
<li>模块之中，可以使用<code>import</code>命令加载其他模块(<code>.js</code>后缀不可省略，需要提供绝对 URL 或相对 URL)，也可以使用<code>export</code>命令输出对外接口。</li>
<li>模块之中，顶层的<code>this</code>关键字返回<code>undefined</code>，而不是指向<code>window</code>。也就是说，在模块顶层使用<code>this</code>关键字，是无意义的。</li>
<li>同一个模块如果加载多次，将只执行一次。</li>
</ul><hr /><p>下面是一个示例模块。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import utils from 'https://example.com/js/utils.js'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const x = 1</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log(x === window.x) //false</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console.log(this === undefined) // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>利用顶层的<code>this</code>等于<code>undefined</code>这个语法点，可以侦测当前代码是否在 ES6 模块之中</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const isNotModuleScript = this !== undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>ES6 模块与 CommonJS 模块的差异<a href="#es6-模块与-commonjs-模块的差异"><span>#</span></a></h2><p>讨论 Node 加载 ES6 模块之前，必须了解 ES6 模块与 CommonJS 模块完全不同。</p><p>它们有两个重大差异。</p><ul>
<li>CommonJS 模块输出的是一个值的拷贝，ES6 模块输出的是值的引用。</li>
<li>CommonJS 模块是运行时加载，ES6 模块是编译时输出接口。</li>
</ul><p>第二个差异是因为 CommonJS 加载的是一个对象(即<code>module.exports</code>属性)，该对象只有在脚本运行完才会生成。而 ES6 模块不是对象，它的对外接口只是一种静态定义，在代码静态解析阶段就会生成。</p><p>下面重点解释第一个差异。</p><p>CommonJS 模块输出的是值的拷贝，也就是说，一旦输出一个值，模块内部的变化就影响不到这个值。请看下面这个模块文件<code>lib.js</code>的例子</p><div><figure><figcaption><span>lib.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var counter = 3</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>function incCounter() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>counter++</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>module.exports = {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>counter: counter,</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>incCounter: incCounter,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码输出内部变量<code>counter</code>和改写这个变量的内部方法<code>incCounter</code>。然后，在<code>main.js</code>里面加载这个模块</p><div><figure><figcaption><span>main.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var mod = require("./lib")</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>console.log(mod.counter) // 3</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>mod.incCounter()</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console.log(mod.counter) // 3</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码说明，<code>lib.js</code>模块加载以后，它的内部变化就影响不到输出的<code>mod.counter</code>了。这是因为<code>mod.counter</code>是一个原始类型的值，会被缓存。除非写成一个函数，才能得到内部变动后的值</p><div><figure><figcaption><span>lib.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var counter = 3</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>function incCounter() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>counter++</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>module.exports = {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>get counter() {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>return counter</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>incCounter: incCounter,</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，输出的<code>counter</code>属性实际上是一个取值器函数。现在再执行<code>main.js</code>，就可以正确读取内部变量<code>counter</code>的变动了</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ node main.js</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>3</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候，遇到模块加载命令<code>import</code>，就会生成一个只读引用。等到脚本真正执行时，再根据这个只读引用，到被加载的那个模块里面去取值。
换句话说，ES6 的<code>import</code>有点像 Unix 系统的“符号连接”，原始值变了，<code>import</code>加载的值也会跟着变。因此，ES6 模块是动态引用，并且不会缓存值，模块里面的变量绑定其所在的模块</p><div><figure><figcaption><span>lib.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export let counter = 3</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export function incCounter() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>counter++</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// main.js</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>import { counter, incCounter } from './lib'</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console.log(counter) // 3</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>incCounter()</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>console.log(counter) // 4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码说明，ES6 模块输入的变量<code>counter</code>是活的，完全反应其所在模块<code>lib.js</code>内部的变化。</p><hr /><p>例：<code>m1.js</code>的变量<code>foo</code>，在刚加载时等于bar，过了 500 毫秒，又变为等于baz</p><div><figure><figcaption><span>m1.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export var foo = 'bar'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>setTimeout(() =&gt; (foo = 'baz'), 500)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// m2.js</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>import { foo } from './m1.js'</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console.log(foo)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>setTimeout(() =&gt; console.log(foo), 500)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>m1.js</code>的变量<code>foo</code>，在刚加载时等于<code>bar</code>，过了 500 毫秒，又变为等于<code>baz</code></p><p>让我们看看，<code>m2.js</code>能否正确读取这个变化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ babel-node m2.js</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>bar</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>baz</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码表明，ES6 模块不会缓存运行结果，而是动态地去被加载的模块取值，并且变量总是绑定其所在的模块</p><p>由于 ES6 输入的模块变量，只是一个“符号连接”，所以这个变量是只读的，对它进行重新赋值会报错</p><div><figure><figcaption><span>lib.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export let obj = {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// main.js</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import { obj } from './lib'</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>obj.prop = 123 // OK</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>obj = {} // TypeError</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>main.js</code>从<code>lib.js</code>输入变量<code>obj</code>，可以对<code>obj</code>添加属性，但是重新赋值就会报错。因为变量<code>obj</code>指向的地址是只读的，不能重新赋值，这就好比<code>main.js</code>创造了一个名为<code>obj</code>的<code>const</code>变量。</p><p>最后，<code>export</code>通过接口，输出的是同一个值。不同的脚本加载这个接口，得到的都是同样的实例</p><div><figure><figcaption><span>mod.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function C() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>this.sum = 0</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>this.add = function () {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>this.sum += 1</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>this.show = function () {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>console.log(this.sum)</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>export let c = new C()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的脚本<code>mod.js</code>，输出的是一个<code>C</code>的实例。不同的脚本加载这个模块，得到的都是同一个实例。</p><div><figure><figcaption><span>x.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import { c } from './mod'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>c.add()</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// y.js</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>import { c } from './mod'</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>c.show()</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// main.js</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>import './x'</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>import './y'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>现在执行<code>main.js</code>，输出的是1。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ babel-node main.js</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这就证明了<code>x.js</code>和<code>y.js</code>加载的都是<code>C</code>的同一个实例</p></section>
<section><h2>Node 加载<a href="#node-加载"><span>#</span></a></h2><section><h3>概述<a href="#概述"><span>#</span></a></h3><p>Node 对 ES6 模块的处理比较麻烦，因为它有自己的 CommonJS 模块格式，与 ES6 模块格式是不兼容的。目前的解决方案是，将两者分开，ES6 模块和 CommonJS 采用各自的加载方案。</p><p>Node 要求 ES6 模块采用<code>.mjs</code>后缀文件名。也就是说，只要脚本文件里面使用<code>import</code>或者<code>export</code>命令，那么就必须采用<code>.mjs</code>后缀名。<code>require</code>命令不能加载<code>.mjs</code>文件，会报错，只有<code>import</code>命令才可以加载<code>.mjs</code>文件。反过来，<code>.mjs</code>文件里面也不能使用<code>require</code>命令，必须使用<code>import</code></p><p>为了与浏览器的<code>import</code>加载规则相同，Node 的<code>.mjs</code>文件支持 URL 路径</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import "./foo?query=1" // 加载 ./foo 传入参数 ?query=1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，脚本路径带有参数<code>?query=1</code>，Node 会按 URL 规则解读。同一个脚本只要参数不同，就会被加载多次，并且保存成不同的缓存。由于这个原因，只要文件名中含有<code>:</code>、<code>%</code>、<code>#</code>、<code>?</code>等特殊字符，最好对这些字符进行转义。</p><p>目前，Node 的<code>import</code>命令只支持加载本地模块(<code>file:</code>协议)，不支持加载远程模块。 如果模块名不含路径，那么<code>import</code>命令会去<code>node_modules</code>目录寻找这个模块</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import 'baz'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>import 'abc/123'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果模块名包含路径，那么<code>import</code>命令会按照路径去寻找这个名字的脚本文件</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import 'file:///etc/config/app.json'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>import './foo'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>import './foo?search'</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import '../bar'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>import '/baz'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果脚本文件省略了后缀名，比如<code>import './foo'</code>，Node 会依次尝试四个后缀名: <code>./foo.mjs</code>、<code>./foo.js</code>、<code>./foo.json</code>、<code>./foo.node</code>。如果这些脚本文件都不存在，Node 就会去加载<code>./foo/package.json</code>的<code>main</code>字段指定的脚本。
如果<code>./foo/package.json</code>不存在或者没有main字段，那么就会依次加载<code>./foo/index.mjs</code>、<code>./foo/index.js</code>、<code>./foo/index.json</code>、<code>./foo/index.node</code>。如果以上四个文件还是都不存在，就会抛出错误。</p><p>最后，Node 的<code>import</code>命令是异步加载，这一点与浏览器的处理方法相同</p></section><section><h3>内部变量<a href="#内部变量"><span>#</span></a></h3><p>ES6 模块应该是通用的，同一个模块不用修改，就可以用在浏览器环境和服务器环境。为了达到这个目标，Node 规定 ES6 模块之中不能使用 CommonJS 模块的特有的一些内部变量。</p><p>首先，就是<code>this</code>关键字。ES6 模块之中，顶层的<code>this</code>指向<code>undefined</code>；CommonJS 模块的顶层<code>this</code>指向当前模块，这是两者的一个重大差异。</p><p>其次，以下这些顶层变量在 ES6 模块之中都是不存在的</p><ul>
<li><code>arguments</code></li>
<li><code>require</code></li>
<li><code>module</code></li>
<li><code>exports</code></li>
<li><code>__filename</code></li>
<li><code>__dirname</code></li>
</ul><p>如果您一定要使用这些变量，有一个变通方法，就是写一个 CommonJS 模块输出这些变量，然后再用 ES6 模块加载这个 CommonJS 模块。但是这样一来，该 ES6 模块就不能直接用于浏览器环境了，所以不推荐这样做</p><div><figure><figcaption><span>expose.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>module.exports = { __dirname }</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// use.mjs</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import expose from './expose.js'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>const { __dirname } = expose</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>expose.js</code>是一个 CommonJS 模块，输出变量<code>__dirname</code>，该变量在 ES6 模块之中不存在。ES6 模块加载<code>expose.js</code>，就可以得到<code>__dirname</code></p></section><section><h3>ES6 模块加载 CommonJS 模块<a href="#es6-模块加载-commonjs-模块"><span>#</span></a></h3><p>CommonJS 模块的输出都定义在<code>module.exports</code>这个属性上面。Node 的<code>import</code>命令加载 CommonJS 模块，Node 会自动将<code>module.exports</code>属性，当作模块的默认输出，即等同于<code>export default xxx</code>。</p><p>下面是一个 CommonJS 模块。</p><div><figure><figcaption><span>a.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>module.exports = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>foo: 'hello',</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>bar: 'world',</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>export default {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>foo: 'hello',</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>bar: 'world',</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>import</code>命令加载上面的模块，<code>module.exports</code>会被视为默认输出，<code>即import</code>命令实际上输入的是这样一个对象<code>{ default: module.exports }</code>。</p><p>所以，一共有三种写法，可以拿到 CommonJS 模块的<code>module.exports</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 写法一</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>import baz from './a'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// baz = {foo: 'hello', bar: 'world'};</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 写法二</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>import { default as baz } from './a'</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// baz = {foo: 'hello', bar: 'world'};</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// 写法三</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>import * as baz from './a'</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// baz = {</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>//   get default() {return module.exports;},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>//   get foo() {return this.default.foo}.bind(baz),</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>//   get bar() {return this.default.bar}.bind(baz)</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>// }</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>上面代码的第三种写法，可以通过<code>baz.default</code>拿到<code>module.exports</code>。<code>foo</code>属性和<code>bar</code>属性就是可以通过这种方法拿到了<code>module.exports</code><a href="#上面代码的第三种写法可以通过bazdefault拿到moduleexportsfoo属性和bar属性就是可以通过这种方法拿到了moduleexports"><span>#</span></a></h2><p>下面是一些例子</p><div><figure><figcaption><span>b.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>module.exports = null</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// es.js</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import foo from './b'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// foo = null</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>import * as bar from './b'</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// bar = { default:null }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>es.js</code>采用第二种写法时，要通过<code>bar.default</code>这样的写法，才能拿到<code>module.exports</code>。</p><div><figure><figcaption><span>c.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>module.exports = function two() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return 2</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// es.js</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>import foo from './c'</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>foo() // 2</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>import * as bar from './c'</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>bar.default() // 2</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>bar() // throws, bar is not a function</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>bar</code>本身是一个对象，不能当作函数调用，只能通过<code>bar.default</code>调用。</p><p>CommonJS 模块的输出缓存机制，在 ES6 加载方式下依然有效</p><div><figure><figcaption><span>foo.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>module.exports = 123</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>setTimeout((_) =&gt; (module.exports = null))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，对于加载<code>foo.js</code>的脚本，<code>module.exports</code>将一直是<code>123</code>，而不会变成<code>null</code>。</p><p>由于 ES6 模块是编译时确定输出接口，CommonJS 模块是运行时确定输出接口，所以采用<code>import</code>命令加载 CommonJS 模块时，不允许采用下面的写法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 不正确</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>import { readFile } from "fs"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的写法不正确，因为<code>fs</code>是 CommonJS 格式，只有在运行时才能确定<code>readFile</code>接口，而<code>import</code>命令要求编译时就确定这个接口。解决方法就是改为整体输入</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 正确的写法一</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>import * as express from 'express'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const app = express.default()</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 正确的写法二</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>import express from 'express'</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>const app = express()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>CommonJS 模块加载 ES6 模块<a href="#commonjs-模块加载-es6-模块"><span>#</span></a></h3><p>CommonJS 模块加载 ES6 模块，不能使用<code>require</code>命令，而要使用<code>import()</code>函数。ES6 模块的所有输出接口，会成为输入对象的属性</p><div><figure><figcaption><span>es.mjs</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let foo = { bar: 'my-default' }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export default foo</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// cjs.js</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>const es_namespace = await import('./es.mjs')</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// es_namespace = {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>//     get default() {},</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// }</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console.log(es_namespace.default)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// { bar:'my-default' }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>default</code>接口变成了<code>es_namespace.default</code>属性</p><hr /><p>下面是另一个例子</p><div><div><div><figure><figcaption><span>es.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export let foo = { bar: 'my-default' }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export { foo as bar }</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export function f() {}</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>export class c {}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// cjs.js</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>const es_namespace = await import('./es')</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// es_namespace = {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>//     get foo() {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>//         return foo</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>//     },</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>//     get bar() {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>//         return foo</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>//     },</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>//     get f() {</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>//         return f</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>//     },</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>//     get c() {</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>//         return c</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>//     },</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>// }</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>循环加载<a href="#循环加载"><span>#</span></a></h2><p>“循环加载”(circular dependency)指的是，<code>a</code>脚本的执行依赖<code>b</code>脚本，而<code>b</code>脚本的执行又依赖<code>a</code>脚本</p><div><figure><figcaption><span>a.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var b = require('b')</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// b.js</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>var a = require('a')</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>通常，“循环加载”表示存在强耦合，如果处理不好，还可能导致递归加载，使得程序无法执行，因此应该避免出现。</p><p>但是实际上，这是很难避免的，尤其是依赖关系复杂的大项目，很容易出现<code>a</code>依赖<code>b</code>，<code>b</code>依赖<code>c</code>，<code>c</code>又依赖<code>a</code>这样的情况。这意味着，模块加载机制必须考虑“循环加载”的情况。</p><p>对于 JavaScript 语言来说，目前最常见的两种模块格式 CommonJS 和 ES6，处理“循环加载”的方法是不一样的，返回的结果也不一样</p><section><h3>CommonJS 模块的加载原理<a href="#commonjs-模块的加载原理"><span>#</span></a></h3><p>介绍 ES6 如何处理“循环加载”之前，先介绍目前最流行的 CommonJS 模块格式的加载原理。</p><p>CommonJS 的一个模块，就是一个脚本文件。<code>require</code>命令第一次加载该脚本，就会执行整个脚本，然后在内存生成一个对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>id: '...',</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>exports: {},</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>loaded: true,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码就是 Node 内部加载模块后生成的一个对象。该对象的<code>id</code>属性是模块名，<code>exports</code>属性是模块输出的各个接口，<code>loaded</code>属性是一个布尔值，表示该模块的脚本是否执行完毕。其他还有很多属性，这里都省略了。</p><p>以后需要用到这个模块的时候，就会到<code>exports</code>属性上面取值。即使再次执行<code>require</code>命令，也不会再次执行该模块，而是到缓存之中取值。也就是说，CommonJS 模块无论加载多少次，都只会在第一次加载时运行一次，以后再加载，就返回第一次运行的结果，除非手动清除系统缓存</p></section><section><h3>CommonJS 模块的循环加载<a href="#commonjs-模块的循环加载"><span>#</span></a></h3><p>CommonJS 模块的重要特性是加载时执行，即脚本代码在require的时候，就会全部执行。一旦出现某个模块被”循环加载”，就只输出已经执行的部分，还未执行的部分不会输出。</p><p>让我们来看，Node <a href="https://nodejs.org/api/modules.html#modules_cycles" target="_blank">官方文档</a> 里面的例子。脚本文件a.js代码如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>exports.done = false</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var b = require('./b.js')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log('在 a.js 之中,b.done = %j', b.done)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>exports.done = true</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log('a.js 执行完毕')</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码之中，<code>a.js</code>脚本先输出一个<code>done</code>变量，然后加载另一个脚本文件<code>b.js</code>。注意，此时<code>a.js</code>代码就停在这里，等待<code>b.js</code>执行完毕，再往下执行。</p><p>再看<code>b.js</code>的代码</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>exports.done = false</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var a = require('./a.js')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log('在 b.js 之中，a.done = %j', a.done)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>exports.done = true</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log('b.js 执行完毕')</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码之中，<code>b.js</code>执行到第二行，就会去加载<code>a.js</code>，这时，就发生了“循环加载”。系统会去<code>a.js</code>模块对应对象的<code>exports</code>属性取值，可是因为<code>a.js</code>还没有执行完，从<code>exports</code>属性只能取回已经执行的部分，而不是最后的值。</p><p><code>a.js</code>已经执行的部分，只有一行。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>exports.done = false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因此，对于<code>b.js</code>来说，它从<code>a.js</code>只输入一个变量<code>done</code>，值为<code>false</code>。</p><p>然后，<code>b.js</code>接着往下执行，等到全部执行完毕，再把执行权交还给<code>a.js</code>。于是，<code>a.js</code>接着往下执行，直到执行完毕。我们写一个脚本<code>main.js</code>，验证这个过程。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var a = require('./a.js')</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var b = require('./b.js')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>执行<code>main.js</code>，运行结果如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ node main.js</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>在 b.js 之中，a.done = false</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>b.js 执行完毕</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>在 a.js 之中，b.done = true</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>a.js 执行完毕</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>在 main.js 之中, a.done=true, b.done=true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的代码证明了两件事。一是，在<code>b.js</code>之中，<code>a.js</code>没有执行完毕，只执行了第一行。二是，<code>main.js</code>执行到第二行时，不会再次执行<code>b.js</code>，而是输出缓存的<code>b.js</code>的执行结果，即它的第四行。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>exports.done = true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>总之，CommonJS 输入的是被输出值的拷贝，不是引用。</p><p>另外，由于 CommonJS 模块遇到循环加载时，返回的是当前已经执行的部分的值，而不是代码全部执行后的值，两者可能会有差异。所以，输入变量的时候，必须非常小心</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var a = require('a') // 安全的写法</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var foo = require('a').foo // 危险的写法</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>exports.good = function (arg) {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>return a.foo('good', arg) // 使用的是 a.foo 的最新值</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>exports.bad = function (arg) {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>return foo('bad', arg) // 使用的是一个部分加载时的值</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，如果发生循环加载，<code>require('a').foo</code>的值很可能后面会被改写，改用<code>require('a')</code>会更保险一点</p></section><section><h3>ES6 模块的循环加载<a href="#es6-模块的循环加载"><span>#</span></a></h3><p>ES6 处理“循环加载”与 CommonJS 有本质的不同。ES6 模块是动态引用，如果使用import从一个模块加载变量(即<code>import foo from 'foo'</code>)，那些变量不会被缓存，而是成为一个指向被加载模块的引用，需要开发者自己保证，真正取值的时候能够取到值。</p><p>请看下面这个例子。</p><div><figure><figcaption><span>a.mjs</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import { bar } from './b'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log('a.mjs')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log(bar)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>export let foo = 'foo'</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// b.mjs</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>import { foo } from './a'</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console.log('b.mjs')</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console.log(foo)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>export let bar = 'bar'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>a.mjs</code>加载<code>b.mjs</code>，<code>b.mjs</code>又加载<code>a.mjs</code>，构成循环加载。执行<code>a.mjs</code>，结果如下。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ node --experimental-modules a.mjs</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>b.mjs</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>ReferenceError: foo is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，执行<code>a.mjs</code>以后会报错，<code>foo</code>变量未定义，这是为什么？</p><p>让我们一行行来看，ES6 循环加载是怎么处理的。首先，执行<code>a.mjs</code>以后，引擎发现它加载了<code>b.mjs</code>，因此会优先执行<code>b.mjs</code>，然后再执行<code>a.mjs</code>。接着，执行<code>b.mjs</code>的时候，已知它从<code>a.mjs</code>输入了<code>foo</code>接口，这时不会去执行<code>a.mjs</code>，而是认为这个接口已经存在了，继续往下执行。执行到第三行<code>console.log(foo)</code>的时候，才发现这个接口根本没定义，因此报错。</p><p>解决这个问题的方法，就是让<code>b.mjs</code>运行的时候，<code>foo</code>已经有定义了。这可以通过将<code>foo</code>写成函数来解决。</p><div><div><div><figure><figcaption><span>a.mjs</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import { bar } from './b'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log('a.mjs')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log(bar())</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>function foo() {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>return 'foo'</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>export { foo }</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// b.mjs</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>import { foo } from './a'</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console.log('b.mjs')</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>console.log(foo())</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>function bar() {</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>return 'bar'</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>export { bar }</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这时再执行<code>a.mjs</code>就可以得到预期结果</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ node --experimental-modules a.mjs</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>b.mjs</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>foo</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>a.mjs</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>bar</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这是因为函数具有提升作用，在执行<code>import {bar} from './b'</code>时，函数<code>foo</code>就已经有定义了，所以<code>b.mjs</code>加载的时候不会报错。这也意味着，如果把函数<code>foo</code>改写成函数表达式，也会报错</p><div><figure><figcaption><span>a.mjs</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import { bar } from './b'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log('a.mjs')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log(bar())</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>const foo = () =&gt; 'foo'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>export { foo }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码的第四行，改成了函数表达式，就不具有提升作用，执行就会报错。</p><p>我们再来看 ES6 模块加载器SystemJS给出的一个例子</p><div><figure><figcaption><span>even.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import { odd } from './odd'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export var counter = 0</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export function even(n) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>counter++</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>return n === 0 || odd(n - 1)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// odd.js</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>import { even } from './even'</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>export function odd(n) {</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>return n !== 0 &amp;&amp; even(n - 1)</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>even.js</code>里面的函数<code>even</code>有一个参数<code>n</code>，只要不等于 0，就会减去 1，传入加载的<code>odd()</code>。<code>odd.js</code>也会做类似操作。</p><p>运行上面这段代码，结果如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ babel-node</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&gt; import * as m from './even.js';</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&gt; m.even(10);</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>true</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&gt; m.counter</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>6</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&gt; m.even(20)</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>true</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&gt; m.counter</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>17</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，参数<code>n</code>从 10 变为 0 的过程中，<code>even()</code>一共会执行 6 次，所以变量<code>counter</code>等于 6。第二次调用<code>even()</code>时，参数<code>n</code>从 20 变为 0，<code>even()</code>一共会执行 11 次，加上前面的 6 次，所以变量<code>counter</code>等于 17。</p><p>这个例子要是改写成 CommonJS，就根本无法执行，会报错</p><div><figure><figcaption><span>even.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var odd = require('./odd')</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var counter = 0</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>exports.counter = counter</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>exports.even = function (n) {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>counter++</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>return n == 0 || odd(n - 1)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// odd.js</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>var even = require('./even').even</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>module.exports = function (n) {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>return n != 0 &amp;&amp; even(n - 1)</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>even.js</code>加载<code>odd.js</code>，而<code>odd.js</code>又去加载<code>even.js</code>，形成“循环加载”。这时，执行引擎就会输出<code>even.js</code>已经执行的部分(不存在任何结果)，所以在<code>odd.js</code>之中，变量<code>even</code>等于<code>undefined</code>，等到后面调用<code>even(n - 1)</code>就会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ node</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&gt; var m = require('./even');</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&gt; m.even(10)</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>TypeError: even is not a function</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>ES6 模块的转码<a href="#es6-模块的转码"><span>#</span></a></h2><p>浏览器目前还不支持 ES6 模块，为了现在就能使用，可以将转为 ES5 的写法。除了 Babel 可以用来转码之外，还有以下两个方法，也可以用来转码</p><section><h3>ES6 module transpiler<a href="#es6-module-transpiler"><span>#</span></a></h3><p><a href="https://github.com/esnext/es6-module-transpiler" target="_blank">ES6 module transpiler</a>是 square 公司开源的一个转码器，可以将 ES6 模块转为 CommonJS 模块或 AMD 模块的写法，从而在浏览器中使用</p><p>首先，安装这个转码器。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>npm install -g es6-module-transpiler</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后，使用<code>compile-modules convert</code>命令，将 ES6 模块文件转码。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>compile-modules convert file1.js file2.js</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>-o</code>参数可以指定转码后的文件名。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>compile-modules convert -o out.js file1.js</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>SystemJS<a href="#systemjs"><span>#</span></a></h3><p>另一种解决方法是使用 <a href="https://github.com/systemjs/systemjs" target="_blank">SystemJS</a> 。它是一个垫片库(polyfill)，可以在浏览器内加载 ES6 模块、AMD 模块和 CommonJS 模块，将其转为 ES5 格式。它在后台调用的是 Google 的 Traceur 转码器。</p><p>使用时，先在网页内载入system.js文件</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script src="system.js"&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后，使用<code>System.import</code>方法加载模块文件。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>System.import("./app.js")</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中的<code>./app</code>，指的是当前目录下的 <code>app.js</code> 文件。它可以是 ES6 模块文件，<code>System.import</code>会自动将其转码。</p><p>需要注意的是，<code>System.import</code>使用异步加载，返回一个 Promise 对象，可以针对这个对象编程。</p><p>下面是一个模块文件。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// app/es6-file.js:</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export class q {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>constructor() {</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>this.es6 = 'hello'</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后，在网页内加载这个模块文件</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script&gt;</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>System.import('app/es6-file').then(function (m) {</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>console.log(new m.q().es6) // hello</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>System.import方</code>法返回的是一个 Promise 对象，所以可以用<code>then</code>方法指定回调函数。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/module-mo-kuai-hua/</id>
      <title type="text">Module 模块化</title>
      <published>2022-07-22T00:32:08.000Z</published>
      <updated>2022-07-22T00:32:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/module-mo-kuai-hua/"/>
      <summary type="text">历史上，JavaScript 一直没有模块( module )体系，无法将一个大程序拆分成互相依赖的小文件，再用简单的方法拼装起来。其他语言都有这项功能，比如 Ruby 的 require 、Python 的 import ，甚至就连 CSS 都有 @import ，但是 JavaScript 任何…</summary>
      <content type="html"><![CDATA[<section><h1>模块化 module<a href="#模块化-module"><span>#</span></a></h1><section><h2>基本概述<a href="#基本概述"><span>#</span></a></h2><p>历史上，JavaScript 一直没有模块(<code>module</code>)体系，无法将一个大程序拆分成互相依赖的小文件，再用简单的方法拼装起来。其他语言都有这项功能，比如 Ruby 的<code>require</code>、Python 的<code>import</code>，甚至就连 CSS 都有<code>@import</code>，但是 JavaScript 任何这方面的支持都没有，这对开发大型的、复杂的项目形成了巨大障碍。</p><p>在 ES6 之前，社区制定了一些模块加载方案，最主要的有 CommonJS 和 AMD 两种。前者用于服务器，后者用于浏览器。ES6 在语言标准的层面上，实现了模块功能，而且实现得相当简单，完全可以取代 CommonJS 和 AMD 规范，成为浏览器和服务器通用的模块解决方案</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// CommonJS模块</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let { stat, exists, readFile } = require('fs')</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>let _fs = require('fs')</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>let stat = _fs.stat</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>let exists = _fs.exists</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>let readfile = _fs.readfile</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码的实质是整体加载<code>fs</code>模块(即加载<code>fs</code>的所有方法)，生成一个对象(<code>_fs</code>)，然后再从这个对象上面读取 3 个方法。这种加载称为“<strong>运行时加载</strong>”，因为只有运行时才能得到这个对象，导致完全没办法在编译时做“静态优化”</p><p>ES6 模块不是对象，而是通过<code>export</code>命令显式指定输出的代码，再通过<code>import</code>命令输入</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// ES6模块</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>import { stat, exists, readFile } from "fs"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码的实质是从fs模块加载 3 个方法，其他方法不加载。这种加载称为“<strong>编译时加载</strong>”或者静态加载，即 ES6 可以在编译时就完成模块加载，效率要比 CommonJS 模块的加载方式高。当然，这也导致了没法引用 ES6 模块本身，因为它不是对象</p><p>由于 ES6 模块是编译时加载，使得静态分析成为可能。有了它，就能进一步拓宽 JavaScript 的语法，比如引入宏(<code>macro</code>)和类型检验(<code>type system</code>)这些只能靠静态分析实现的功能。
除了静态加载带来的各种好处，ES6 模块还有以下好处。</p><ul>
<li>不再需要UMD模块格式了，将来服务器和浏览器都会支持 ES6 模块格式。目前，通过各种工具库，其实已经做到了这一点。</li>
<li>将来浏览器的新 API 就能用模块格式提供，不再必须做成全局变量或者<code>navigator</code>对象的属性。</li>
<li>不再需要对象作为命名空间(比如<code>Math</code>对象)，未来这些功能可以通过模块提供。</li>
</ul></section><section><h2>严格模式<a href="#严格模式"><span>#</span></a></h2><p>ES6 的模块自动采用严格模式，不管您有没有在模块头部加上<code>"use strict"</code>。</p><p>严格模式主要有以下限制。</p><ul>
<li>变量必须声明后再使用</li>
<li>函数的参数不能有同名属性，否则报错</li>
<li>不能使用<code>with</code>语句 不能对只读属性赋值，否则报错</li>
<li>不能使用前缀 0 表示八进制数，否则报错</li>
<li>不能删除不可删除的属性，否则报错</li>
<li>不能删除变量<code>delete prop</code>，会报错，只能删除属性<code>delete global[prop]</code></li>
<li><code>eval</code>不会在它的外层作用域引入变量</li>
<li><code>eval</code>和<code>arguments</code>不能被重新赋值</li>
<li><code>arguments</code>不会自动反映函数参数的变化</li>
<li>不能使用<code>arguments.callee</code></li>
<li>不能使用<code>arguments.caller </code></li>
<li>禁止<code>this</code>指向全局对象</li>
<li>不能使用<code>fn.caller</code>和<code>fn.arguments</code>获取函数调用的堆栈</li>
<li>增加了保留字(比如<code>protected</code>、<code>static</code>和<code>interface</code>)</li>
</ul><p>上面这些限制，模块都必须遵守。
其中，尤其需要注意<code>this</code>的限制。ES6 模块之中，顶层的<code>this</code>指向<code>undefined</code>，即不应该在顶层代码使用<code>this</code></p></section><section><h2>export 命令<a href="#export-命令"><span>#</span></a></h2><p>模块功能主要由两个命令构成: <code>export</code>和<code>import</code>。</p><ul>
<li><code>export</code>命令用于规定模块的对外接口</li>
<li><code>import</code>命令用于输入其他模块提供的功能。</li>
</ul><p>一个模块就是一个独立的文件。该文件内部的所有变量，外部无法获取。
如果您希望外部能够读取模块内部的某个变量，就必须使用<code>export</code>关键字输出该变量。</p><section><h3>分别暴露<a href="#分别暴露"><span>#</span></a></h3><p>下面是一个 JS 文件，里面使用<code>export</code>命令输出变量，在每个需要暴露的变量前加上<code>export</code>关键字</p><div><figure><figcaption><span>profile.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export var firstName = 'Michael'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export var lastName = 'Jackson'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export function fun() {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>console.log("fun");</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码是<code>profile.js</code>文件，保存了用户信息。ES6 将其视为一个模块，里面用<code>export</code>命令对外部输出了三个变量</p></section><section><h3>统一暴露<a href="#统一暴露"><span>#</span></a></h3><p>在<code>export</code>命令后面，使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在<code>var</code>语句前)是等价的</p><div><figure><figcaption><span>profile.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var firstName = 'Michael'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var lastName = 'Jackson'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>function fun() {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>console.log("fun");</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>export { firstName, lastName, fun }</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>注意<a href="#注意"><span>#</span></a></h3><ul>
<li>
<p>通常情况下，<code>export</code>输出的变量就是本来的名字，但是可以使用<code>as</code>关键字重命名</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function v1() {}</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>function v2() {}</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>export {</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>v1 as streamV1,</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>v2 as streamV2,</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>v2 as streamLatestVersion</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码使用<code>as</code>关键字，重命名了函数<code>v1</code>和<code>v2</code>的对外接口。重命名后，<code>v2</code>可以用不同的名字输出两次</p>
</li>
<li>
<p><code>export</code>语句输出的接口，与其对应的值是动态绑定关系，即通过该接口，可以取到模块内部实时的值</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>export var foo = 'bar'</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>setTimeout(() =&gt; (foo = 'baz'), 500)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码输出变量<code>foo</code>，值为<code>bar</code>，500 毫秒之后变成<code>baz</code></p>
</li>
</ul></section></section><section><h2>import 命令<a href="#import-命令"><span>#</span></a></h2><p>使用<code>export</code>命令定义了模块的对外接口以后，其他 JS 文件就可以通过<code>import</code>命令加载这个模块</p><section><h3>解构赋值形式<a href="#解构赋值形式"><span>#</span></a></h3><p><code>import</code>命令接受一对大括号，里面指定要从其他模块导入的变量名。大括号里面的变量名，必须与被导入模块(profile.js)对外接口的名称相同。类似于解构赋值的形式</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import { firstName, lastName, fun } from "./profile.js"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果想为输入的变量重新取一个名字，<code>import</code>命令要使用<code>as</code>关键字，将输入的变量重命名</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import { lastName as surname } from "./profile.js"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>整体加载形式<a href="#整体加载形式"><span>#</span></a></h3><p>除了指定加载某个输出值，还可以使用整体加载，即用星号(<code>*</code>)指定一个对象，所有输出值都加载在这个对象上面</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import * as info from './profile.js'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，模块整体加载所在的那个对象，应该是可以静态分析的，所以不允许运行时改变。
下面的写法都是不允许</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import * as info from './profile.js'</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>info.firstName = 'a'</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>// Cannot assign to read only property 'firstName' of object '[object Module]'</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>export default 命令<a href="#export-default-命令"><span>#</span></a></h2><p>从前面的例子可以看出，使用<code>import</code>命令的时候，用户需要知道所要加载的变量名或函数名，否则无法加载。但是，用户肯定希望快速上手，未必愿意阅读文档，去了解模块有哪些属性和方法。</p><p>为了给用户提供方便，让他们不用阅读文档就能加载模块，就要用到<code>export default</code>命令，为模块指定默认输出</p><div><figure><figcaption><span>export-default.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export default function () {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console.log('foo')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其他模块加载该模块时，<code>import</code>命令可以为该匿名函数指定任意名字</p><div><figure><figcaption><span>import-default.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import customName from './export-default'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>customName() // 'foo'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码的<code>import</code>命令，可以用任意名称指向<code>export-default.js</code>输出的方法，这时就不需要知道原模块输出的函数名。需要注意的是，这时<code>import</code>命令后面，不使用大括号</p><p><code>export default</code>命令用在非匿名函数前，也是可以的</p><div><figure><figcaption><span>export-default.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export default function foo() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console.log('foo')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 或者写成</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>function foo() {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>console.log('foo')</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>export default foo</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>foo</code>函数的函数名<code>foo</code>，在模块外部是无效的。加载的时候，视同匿名函数加载</p><hr /><p>下面比较一下默认输出和正常输出</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 第一组</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export default function crc32() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>// 输出</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>import crc32 from 'crc32' // 输入</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// 第二组</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>export function crc32() {</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>// 输出</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>import { crc32 } from 'crc32' // 输入</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码的两组写法，第一组是使用<code>export default</code>时，对应的<code>import</code>语句不需要使用大括号；第二组是不使用<code>export default</code>时，对应的<code>import</code>语句需要使用大括号。</p><p><code>export default</code>命令用于指定模块的默认输出。显然，一个模块只能有一个默认输出，因此<code>export default</code>命令只能使用一次。所以，<code>import</code> 命令后面才不用加大括号，因为只可能唯一对应<code>export default</code>命令。</p><p>本质上，<code>export default</code>就是输出一个叫做<code>default</code>的变量或方法，然后系统允许您为它取任意名字</p><p>所以，下面的写法是有效的</p><div><figure><figcaption><span>modules.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function add(x, y) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return x * y</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>export { add as default }</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// export default add</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// app.js</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>import { default as foo } from 'modules'</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// import foo from 'modules'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>正是因为<code>export default</code>命令其实只是输出一个叫做<code>default</code>的变量，所以它后面不能跟变量声明语句</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 正确</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export var a = 1</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 正确</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>var a = 1</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>export default a</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// 错误</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>export default var a = 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p>例：
有了<code>export default</code>命令，输入模块时就非常直观了，以输入 lodash 模块为例</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import _ from "lodash"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果想在一条import语句中，同时输入默认方法和其他接口，可以写成下面这样</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import _, { each, each as forEach } from "lodash"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对应上面代码的<code>export</code>语句如下。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export default function (obj) {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export function each(obj, iterator, context) {}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>export { each as forEach }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码的最后一行的意思是，暴露出<code>forEach</code>接口，默认指向<code>each</code>接口，即<code>forEach</code>和<code>each</code>指向同一个方法</p><p><code>export default</code>也可以用来输出类</p><div><figure><figcaption><span>MyClass.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export default class {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// main.js</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import MyClass from 'MyClass'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>let o = new MyClass()</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>export 与 import 的复合写法<a href="#export-与-import-的复合写法"><span>#</span></a></h2><p>如果在一个模块之中，先输入后输出同一个模块，<code>import</code>语句可以与<code>export</code>语句写在一起</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export { foo, bar } from 'my_module'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// 可以简单理解为</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import { foo, bar } from 'my_module'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>export { foo, bar }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>export</code>和<code>import</code>语句可以结合在一起，写成一行。但需要注意的是，写成一行以后，<code>foo</code>和<code>bar</code>实际上并没有被导入当前模块，只是相当于对外转发了这两个接口，导致当前模块不能直接使用<code>foo</code>和<code>bar</code></p><p>上面代码中，<code>export</code>和<code>import</code>语句可以结合在一起，写成一行。
但需要注意的是，写成一行以后，<code>foo</code>和<code>bar</code>实际上并没有被导入当前模块，只是相当于对外转发了这两个接口，导致当前模块不能直接使用<code>foo</code>和<code>bar</code></p><p>模块的接口改名和整体输出，也可以采用这种写法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 接口改名</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>export { foo as myFoo } from "my_module"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>// 整体输出</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>export * from "my_module"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>默认接口的写法如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>export { default } from "foo"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>具名接口改为默认接口的写法如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export { es6 as default } from './someModule'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>import { es6 } from './someModule'</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>export default es6</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>默认接口也可以改名为具名接口</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export { default as es6 } from './someModule'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>下面三种import语句，没有对应的复合写法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import * as someIdentifier from 'someModule'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>import someIdentifier from 'someModule'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>import someIdentifier, { namedIdentifier } from 'someModule'</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>模块的继承<a href="#模块的继承"><span>#</span></a></h2><p>模块之间也可以继承。</p><p>假设有一个<code>circleplus</code>模块，继承了<code>circle</code>模块</p><div><figure><figcaption><span>circleplus.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export * from 'circle'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export var e = 2.71828182846</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export default function (x) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>return Math.exp(x)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中的<code>export *</code>，表示再输出<code>circle</code>模块的所有属性和方法。注意，<code>export *</code>命令会忽略<code>circle</code>模块的<code>default</code>方法。然后，上面代码又输出了自定义的<code>e</code>变量和默认方法</p><p>这时，也可以将circle的属性或方法，改名后再输出</p><div><figure><figcaption><span>circleplus.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export { area as circleArea } from 'circle'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码表示，只输出<code>circle</code>模块的<code>area</code>方法，且将其改名为<code>circleArea</code></p><p>加载上面模块的写法如下</p><div><figure><figcaption><span>main.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import * as math from 'circleplus'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>import exp from 'circleplus'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console.log(exp(math.e))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中的<code>import exp</code>表示，将<code>circleplus</code>模块的默认方法加载为<code>exp</code>方法</p></section><section><h2>跨模块常量<a href="#跨模块常量"><span>#</span></a></h2><p>由于<code>const</code>声明的常量只在当前代码块有效，如果想设置跨模块的常量(即跨多个文件)，或者说一个值要被多个模块共享，可以采用下面的写法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// constants.js 模块</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export const A = 1</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>export const B = 3</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>export const C = 4</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// test1.js 模块</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>import * as constants from './constants'</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console.log(constants.A) // 1</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console.log(constants.B) // 3</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// test2.js 模块</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>import { A, B } from './constants'</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>console.log(A) // 1</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>console.log(B) // 3</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果要使用的常量非常多，可以建一个专门的<code>constants</code>目录，将各种常量写在不同的文件里面，保存在该目录下</p><div><figure><figcaption><span>constants/db.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export const db = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>url: 'https://my.couchdbserver.local:5984',</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>admin_username: 'admin',</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>admin_password: 'admin password',</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// constants/user.js</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>export const users = ['root', 'admin', 'staff', 'ceo', 'chief', 'moderator']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后，将这些文件输出的常量，合并在<code>index.js</code>里面</p><div><figure><figcaption><span>constants/index.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>export { db } from './db'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>export { users } from './users'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用的时候，直接加载<code>index.js</code>就可以了。</p><div><figure><figcaption><span>script.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import { db, users } from "./constants/index"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>import()<a href="#import"><span>#</span></a></h2><section><h3>简介<a href="#简介"><span>#</span></a></h3><p>前面介绍过，<code>import</code>命令会被 JavaScript 引擎静态分析，先于模块内的其他语句执行(<code>import</code>命令叫做“连接” binding 其实更合适)。所以，下面的代码会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if (x === 2) {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>import MyModual from './myModual'</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，引擎处理<code>import</code>语句是在编译时，这时不会去分析或执行if语句，所以<code>import</code>语句放在<code>if</code>代码块之中毫无意义，因此会报句法错误，而不是执行时错误。
也就是说，<code>import</code>和<code>export</code>命令只能在模块的顶层，不能在代码块之中(比如，在if代码块之中，或在函数之中)。
这样的设计，固然有利于编译器提高效率，但也导致无法在运行时加载模块。在语法上，条件加载就不可能实现。
如果<code>import</code>命令要取代 Node 的<code>require</code>方法，这就形成了一个障碍。因为<code>require</code>是运行时加载模块，<code>import</code>命令无法取代<code>require</code>的动态加载功能。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const path = './' + fileName</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const myModual = require(path)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的语句就是动态加载，<code>require</code>到底加载哪一个模块，只有运行时才知道。<code>import</code>命令做不到这一点</p><p>因此，有一个提案，建议引入<code>import()</code>函数，完成动态加载</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import(specifier)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>import</code>函数的参数<code>specifier</code>，指定所要加载的模块的位置。<code>import</code>命令能够接受什么参数，<code>import()</code>函数就能接受什么参数，两者区别主要是后者为动态加载</p><hr /><p><code>import()</code>返回一个 Promise 对象。下面是一个例子。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const main = document.querySelector('main')</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>import(`./section-modules/${someVariable}.js`)</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>.then((module) =&gt; {</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>module.loadPageInto(main)</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>.catch((err) =&gt; {</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>main.textContent = err.message</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>import()</code>函数可以用在任何地方，不仅仅是模块，非模块的脚本也可以使用。它是运行时执行，也就是说，什么时候运行到这一句，就会加载指定的模块。另外，<code>import()</code>函数与所加载的模块没有静态连接关系，这点也是与<code>import</code>语句不相同。<code>import()</code>类似于 Node 的<code>require</code>方法，<strong>区别主要是前者是异步加载，后者是同步加载</strong></p></section><section><h3>适用场合<a href="#适用场合"><span>#</span></a></h3><p>下面是<code>import()</code>的一些适用场合</p><section><h4>按需加载<a href="#按需加载"><span>#</span></a></h4><p><code>import()</code>可以在需要的时候，再加载某个模块</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>button.addEventListener('click', (event) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>import('./dialogBox.js')</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>.then((dialogBox) =&gt; {</span></div></div><div><div><div>4</div></div><div><span><span>                </span></span><span>dialogBox.open()</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>})</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>.catch((error) =&gt; {</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>/* Error handling */</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>})</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>import()</code>方法放在<code>click</code>事件的监听函数之中，只有用户点击了按钮，才会加载这个模块</p></section><section><h4>条件加载<a href="#条件加载"><span>#</span></a></h4><p><code>import()</code>可以放在<code>if</code>代码块，根据不同的情况，加载不同的模块</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>if (condition) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>import('moduleA').then()</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>} else {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>import('moduleB').then()</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，如果满足条件，就加载模块 A，否则加载模块 B</p></section><section><h4>动态的模块路径<a href="#动态的模块路径"><span>#</span></a></h4><p><code>import()</code>允许模块路径动态生成</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>import(f()).then()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，根据函数<code>f</code>的返回结果，加载不同的模块</p></section></section><section><h3>注意点<a href="#注意点"><span>#</span></a></h3><p><code>import()</code>加载模块成功以后，这个模块会作为一个对象，当作<code>then</code>方法的参数。因此，可以使用对象解构赋值的语法，获取输出接口</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import('./myModule.js').then(({ export1, export2 }) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>// ...·</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>export1</code>和<code>export2</code>都是<code>myModule.js</code>的输出接口，可以解构获得。</p><p>如果模块有<code>default</code>输出接口，可以用参数直接获得</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>import('./myModule.js').then((myModule) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console.log(myModule.default)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>//上面的代码也可以使用具名输入的形式</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>import('./myModule.js').then(({ default: theDefault }) =&gt; {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>console.log(theDefault)</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果想同时加载多个模块，可以采用下面的写法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Promise.all([</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>import('./module1.js'),</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>import('./module2.js'),</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>import('./module3.js')])</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>.then(([module1, module2, module3]) =&gt; {</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>import()</code>也可以用在 async 函数之中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>async function main() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>const myModule = await import('./myModule.js')</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>const { export1, export2 } = await import('./myModule.js')</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>const [module1, module2, module3] = await Promise.all([</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>import('./module1.js'),</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>import('./module2.js'),</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>import('./module3.js'),</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>])</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>main()</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/dui-xiang-de-kuo-zhan/</id>
      <title type="text">对象的扩展</title>
      <published>2022-07-20T00:57:08.000Z</published>
      <updated>2022-07-20T00:57:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/dui-xiang-de-kuo-zhan/"/>
      <summary type="text">ES6 允许直接写入变量和函数，作为对象的属性和方法。这样的书写更加简洁。 const foo = &quot;bar&quot; const baz = { foo } baz // {foo: &quot;bar&quot;} // 等同于 // const baz = { foo: foo }</summary>
      <content type="html"><![CDATA[<section><h2>属性的简介表示<a href="#属性的简介表示"><span>#</span></a></h2><p>ES6 允许直接写入变量和函数，作为对象的属性和方法。这样的书写更加简洁。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const foo = "bar"</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const baz = { foo }</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>baz // {foo: "bar"}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// const baz = { foo: foo }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码表明，ES6 允许在对象之中，直接写变量。这时，<strong>属性名为变量名, 属性值为变量的值</strong>。下面是另一个例子</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function f(x, y) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return { x, y };</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>function f(x, y) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>return { x: x,  y: y };</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>f(1, 2); // Object {x: 1, y: 2}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>除了属性简写，方法也可以简写，利用方法名加括号替代属性名加函数形式</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const o = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>method() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>return "Hello!"</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>const o = {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>method: function () {</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>return "Hello!"</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>属性名表达式<a href="#属性名表达式"><span>#</span></a></h2><p>JavaScript 定义对象的属性，有两种方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 方法一</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>obj.foo = true</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>// 方法二</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>obj["a" + "bc"] = 123 //{abc: 123}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>方法一是直接用标识符作为属性名，方法二是用表达式作为属性名，这时要将表达式放在方括号之内</p><p>如果使用字面量方式定义对象(使用大括号)，在  ES5 中只能使用方法一(标识符)定义属性。
ES6 允许字面量定义对象时，用方法二(表达式)作为对象的属性名，即把表达式放在方括号内。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let lastWord = 'last word'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const a = {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>'first word': 'hello',</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>[lastWord]: 'world',</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>a['first word'] // "hello"</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>a[lastWord] // "world"</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>a['last word'] // "world"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，属性名表达式如果是一个对象，默认情况下会自动将对象转为字符串<code>[object Object]</code>，这一点要特别小心。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const keyA = { a: 1 };</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const keyB = { b: 2 };</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>const myObject = {</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>[keyA]: "valueA",</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>[keyB]: "valueB",</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>myObject; // Object {[object Object]: "valueB"}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>[keyA]</code>和<code>[keyB]</code>得到的都是<code>[object Object]</code>，所以<code>[keyB]</code>会把<code>[keyA]</code>覆盖掉，而<code>myObject</code>最后只有一个<code>[object Object]</code>属性</p></section>
<section><h2>方法的 name 属性<a href="#方法的-name-属性"><span>#</span></a></h2><p>函数的<code>name</code>属性，返回函数名。对象方法也是函数，因此也有<code>name</code>属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const person = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>sayName() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>console.log('hello!')</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>person.sayName.name // "sayName"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果对象的方法使用了取值函数(getter)和存值函数(setter)，则name属性不是在该方法上面，而是该方法的属性的描述对象的<code>get</code>和<code>set</code>属性上面，返回值是方法名前加上<code>get</code>和<code>set</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>get foo() {},</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>set foo(x) {},</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>obj.foo.name</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// TypeError: Cannot read property 'name' of undefined</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo')</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>descriptor.get.name // "get foo"</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>descriptor.set.name // "set foo"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>有两种特殊情况: <code>bind</code>方法创造的函数，<code>name</code>属性返回<code>bound</code>加上原函数的名字；<code>Function</code>构造函数创造的函数，<code>name</code>属性返回<code>anonymous</code>。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>new Function().name // "anonymous"</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>var doSomething = function () {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>doSomething.bind().name // "bound doSomething"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果对象的方法是一个 Symbol 值，那么<code>name</code>属性返回的是这个 Symbol 值的描述。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const key1 = Symbol('description')</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const key2 = Symbol()</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>let obj = {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>[key1]() {},</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>[key2]() {},</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>obj[key1].name // "[description]"</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>obj[key2].name // ""</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Object.is()<a href="#objectis"><span>#</span></a></h2><p>ES5 比较两个值是否相等，只有两个运算符: 相等运算符(<code>==</code>)和严格相等运算符(<code>===</code>)。它们都有缺点，前者会自动转换数据类型，后者的<code>NaN</code>不等于自身，以及<code>+0</code>等于<code>-0</code>。
JavaScript 缺乏一种运算，在所有环境中，只要两个值是一样的，它们就应该相等。</p><p>ES6 提出“Same-value equality”(同值相等)算法，用来解决这个问题。<code>Object.is</code>就是部署这个算法的新方法。它用来比较两个值是否严格相等，与严格比较运算符(<code>===</code>)的行为基本一致。
不同之处只有两个: 一是<code>+0</code>不等于<code>-0</code>，二是<code>NaN</code>等于自身。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.is('foo', 'foo')  // true</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.is({}, {})   // false</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>+0 === -0 //true</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>NaN === NaN // false</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>Object.is(+0, -0) // false</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Object.is(NaN, NaN) // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>ES5 可以通过下面的代码，部署<code>Object.is</code>。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.defineProperty(Object, 'is', {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>value: function (x, y) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>if (x === y) {</span></div></div><div><div><div>4</div></div><div><span><span>                </span></span><span>// 针对+0 不等于 -0的情况</span></div></div><div><div><div>5</div></div><div><span><span>                </span></span><span>return x !== 0 || 1 / x === 1 / y</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>// 针对NaN的情况</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>return x !== x &amp;&amp; y !== y</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>configurable: true,</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>enumerable: false,</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>writable: true,</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Object.assign()<a href="#objectassign"><span>#</span></a></h2><section><h3>用法<a href="#用法"><span>#</span></a></h3><p><code>Object.assign</code>方法用于对象的合并，将源对象(source)的所有可枚举属性，复制到目标对象(target)。<code>Object.assign</code>方法的第一个参数是目标对象，后面的参数都是源对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const target = { a: 1 }</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const source1 = { b: 2 }</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>const source2 = { c: 3 }</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Object.assign(target, source1, source2)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>target // {a:1, b:2, c:3}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果只有一个参数，<code>Object.assign</code>会直接返回该参数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const obj = { a: 1 }</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Object.assign(obj) === obj // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果该参数不是对象，则会先转成对象，然后返回。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>typeof Object.assign(2) // "object"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>由于<code>undefined</code>和<code>null</code>无法转成对象，所以如果它们作为参数，就会报错。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object.assign(undefined) // 报错</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Object.assign(null) // 报错</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果非对象参数出现在源对象的位置(即非首参数)，那么处理规则有所不同。首先，这些参数都会转成对象，如果无法转成对象，就会跳过。这意味着，如果<code>undefined</code>和<code>null</code>不在首参数，就不会报错。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let obj = { a: 1 };</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Object.assign(obj, undefined) === obj; // true</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Object.assign(obj, null) === obj; // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其他类型的值(即数值、字符串和布尔值)不在首参数，也不会报错。但是，除了字符串会以数组形式，拷贝入目标对象，其他值都不会产生效果。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const v1 = "abc"</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>const v2 = true</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>const v3 = 10</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>const obj = Object.assign({}, v1, v2, v3)</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>console.log(obj) // { "0": "a", "1": "b", "2": "c" }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，v1、v2、v3分别是字符串、布尔值和数值，结果只有字符串合入目标对象(以字符数组的形式)，数值和布尔值都会被忽略。这是因为只有字符串的包装对象，会产生可枚举属性。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object(true) // {[[PrimitiveValue]]: true}</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Object(10) //  {[[PrimitiveValue]]: 10}</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Object("abc") // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，布尔值、数值、字符串分别转成对应的包装对象，可以看到它们的原始值都在包装对象的内部属性<code>[[PrimitiveValue]]</code>上面，这个属性是不会被<code>Object.assign</code>拷贝的。只有字符串的包装对象，会产生可枚举的实义属性，那些属性则会被拷贝。</p><p><code>Object.assign</code>拷贝的属性是有限制的，只拷贝源对象的自身属性(不拷贝继承属性)，也不拷贝不可枚举的属性(<code>enumerable: false</code>)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.assign(</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>{ b: 'c' },</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>Object.defineProperty({}, 'invisible', {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>enumerable: false,</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>value: 'hello',</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// { b: 'c' }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>Object.assign</code>要拷贝的对象只有一个不可枚举属性<code>invisible</code>，这个属性并没有被拷贝进去。 属性名为 Symbol 值的属性，也会被<code>Object.assign</code>拷贝。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object.assign({ a: "b" }, { [Symbol("c")]: "d" })</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// { a: 'b', Symbol(c): 'd' }</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>注意<a href="#注意"><span>#</span></a></h3><section><h4>浅拷贝<a href="#浅拷贝"><span>#</span></a></h4><p><code>Object.assign</code>方法实行的是浅拷贝，而不是深拷贝。也就是说，如果源对象某个属性的值是对象，那么目标对象拷贝得到的是这个对象的引用。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj1 = { a: { b: 1 } }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const obj2 = Object.assign({}, obj1)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>obj1.a.b = 2</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>obj2.a.b // 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，源对象<code>obj1</code>的<code>a</code>属性的值是一个对象，<code>Object.assign</code>拷贝得到的是这个对象的引用。这个对象的任何变化，都会反映到目标对象上面。</p></section><section><h4>同名属性的替换<a href="#同名属性的替换"><span>#</span></a></h4><p>对于这种嵌套的对象，一旦遇到同名属性，<code>Object.assign</code>的处理方法是替换，而不是添加</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const target = { a: { b: 'c', d: 'e' } }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const source = { a: { b: 'hello' } }</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Object.assign(target, source)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// { a: { b: 'hello' } }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>target</code>对象的<code>a</code>属性被<code>source</code>对象的<code>a</code>属性整个替换掉了，而不会得到<code>{ a: { b: 'hello', d: 'e' } }</code>的结果。这通常不是开发者想要的，需要特别小心。
一些函数库提供<code>Object.assign</code>的定制版本(比如 Lodash 的<code>_.defaultsDeep</code>方法)，可以得到深拷贝的合并。</p></section><section><h4>数组的处理<a href="#数组的处理"><span>#</span></a></h4><p><code>Object.assign</code>可以用来处理数组，但是会把数组视为对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object.assign([1, 2, 3], [4, 5]);</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// [4, 5, 3]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>Object.assign</code>把数组视为属性名为 0、1、2 的对象，因此源数组的 0 号属性<code>4</code>覆盖了目标数组的 0 号属性<code>1</code>。</p></section><section><h4>取值函数的处理<a href="#取值函数的处理"><span>#</span></a></h4><p><code>Object.assign</code>只能进行值的复制，如果要复制的值是一个取值函数，那么将求值后再复制。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const source = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>get foo() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>return 1</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>const target = {}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Object.assign(target, source)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// { foo: 1 }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>source</code>对象的<code>foo</code>属性是一个取值函数，<code>Object.assign</code>不会复制这个取值函数，只会拿到值以后，将这个值复制过去。</p></section></section><section><h3>常见用途<a href="#常见用途"><span>#</span></a></h3><section><h4>为对象添加属性<a href="#为对象添加属性"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Point {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>constructor(x, y) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>Object.assign(this, { x, y })</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>// 等价于</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>class Point2 {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>constructor(x, y) {</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>this.x = x</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>this.y = y</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面方法通过<code>Object.assign</code>方法，将x属性和y属性添加到Point类的对象实例。</p></section><section><h4>为对象添加方法<a href="#为对象添加方法"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.assign(SomeClass.prototype, {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>someMethod(arg1, arg2) {},</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>anotherMethod() {},</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// 等同于下面的写法</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>SomeClass.prototype.someMethod = function (arg1, arg2) {}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>SomeClass.prototype.anotherMethod = function () {}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码使用了对象属性的简洁表示法，直接将两个函数放在大括号中，再使用<code>Object.assign</code>方法添加到<code>SomeClass.prototype</code>之中。</p></section><section><h4>克隆对象<a href="#克隆对象"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function clone(origin) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return Object.assign({}, origin)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码将原始对象拷贝到一个空对象，就得到了原始对象的克隆。</p><p>不过，采用这种方法克隆，只能克隆原始对象自身的值，不能克隆它继承的值。如果想要保持继承链，可以采用下面的代码。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function clone(origin) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>let originProto = Object.getPrototypeOf(origin)</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>return Object.assign(Object.create(originProto), origin)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>合并多个对象<a href="#合并多个对象"><span>#</span></a></h4><p>将多个对象合并到某个对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const merge = (target, ...sources) =&gt; Object.assign(target, ...sources)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果希望合并后返回一个新对象，可以改写上面函数，对一个空对象合并。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const merge = (...sources) =&gt; Object.assign({}, ...sources)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>为属性指定默认值<a href="#为属性指定默认值"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const DEFAULTS = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>logLevel: 0,</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>outputFormat: 'html',</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>function processContent(options) {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>options = Object.assign({}, DEFAULTS, options)</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>console.log(options)</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>DEFAULTS</code>对象是默认值，<code>options</code>对象是用户提供的参数。<code>Object.assign</code>方法将<code>DEFAULTS</code>和<code>options</code>合并成一个新对象，如果两者有同名属性，则<code>option</code>的属性值会覆盖<code>DEFAULTS</code>的属性值。</p><p>注意，由于存在浅拷贝的问题，<code>DEFAULTS</code>对象和<code>options</code>对象的所有属性的值，最好都是简单类型，不要指向另一个对象。否则，<code>DEFAULTS</code>对象的该属性很可能不起作用。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const DEFAULTS = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>url: {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>host: 'example.com',</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>port: 7070,</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>processContent({ url: { port: 8000 } })</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>//   url: {port: 8000}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码的原意是将<code>url.port</code>改成 8000，<code>url.host</code>不变。实际结果却是<code>options.url</code>覆盖掉<code>DEFAULTS.url</code>，所以<code>url.host</code>就不存在了</p></section></section></section>
<section><h2>属性的可枚举性和遍历<a href="#属性的可枚举性和遍历"><span>#</span></a></h2><section><h3>可枚举性<a href="#可枚举性"><span>#</span></a></h3><p>对象的每个属性都有一个描述对象(Descriptor)，用来控制该属性的行为。<code>Object.getOwnPropertyDescriptor</code>方法可以获取该属性的描述对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let obj = { foo: 123 }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptor(obj, 'foo')</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>//  {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>//    value: 123,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>//    writable: true,</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>//    enumerable: true,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>//    configurable: true</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>//  }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>描述对象的<code>enumerable</code>属性，称为”可枚举性“，如果该属性为<code>false</code>，就表示某些操作会忽略当前属性</p><p>目前，有四个操作会忽略<code>enumerable</code>为<code>false</code>的属性。</p><ul>
<li><code>for...in</code>循环: 只遍历对象自身的和继承的可枚举的属性。</li>
<li><code>Object.keys()</code>: 返回对象自身的所有可枚举的属性的键名。</li>
<li><code>JSON.stringify()</code>: 只串行化对象自身的可枚举的属性。</li>
<li><code>Object.assign()</code>: 忽略<code>enumerable</code>为<code>false</code>的属性，只拷贝对象自身的可枚举的属性。</li>
</ul><p>这四个操作之中，前三个是 ES5 就有的，最后一个<code>Object.assign()</code>是 ES6 新增的。
其中，只有<code>for...in</code>会返回继承的属性，其他三个方法都会忽略继承的属性，只处理对象自身的属性。
实际上，引入“可枚举”(<code>enumerable</code>)这个概念的最初目的，就是让某些属性可以规避掉<code>for...in</code>操作，不然所有内部属性和方法都会被遍历到。比如，对象原型的<code>toString</code>方法，以及数组的<code>length</code>属性，就通过“可枚举性”，从而避免被<code>for...in</code>遍历到。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// false</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptor([], 'length').enumerable</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>另外，ES6 规定，所有 Class 的原型的方法都是不可枚举的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptor(</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>class { foo() {} }.prototype, 'foo'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>).enumerable</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>总的来说，操作中引入继承的属性会让问题复杂化，大多数时候，我们只关心对象自身的属性。所以，尽量不要用<code>for...in</code>循环，而用<code>Object.keys()</code>代替</p></section><section><h3>属性的遍历<a href="#属性的遍历"><span>#</span></a></h3><p>ES6 一共有 5 种方法可以遍历对象的属性。</p><section><h4>for…in<a href="#forin"><span>#</span></a></h4><p><code>for...in</code>循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。</p></section><section><h4>Object.keys(obj)<a href="#objectkeysobj"><span>#</span></a></h4><p><code>Object.keys</code>返回一个数组，包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。</p></section><section><h4>Object.getOwnPropertyNames(obj)<a href="#objectgetownpropertynamesobj"><span>#</span></a></h4><p><code>Object.getOwnPropertyNames</code>返回一个数组，包含对象自身的所有属性(不含 Symbol 属性，但是包括不可枚举属性)的键名。</p></section><section><h4>Object.getOwnPropertySymbols(obj)<a href="#objectgetownpropertysymbolsobj"><span>#</span></a></h4><p><code>Object.getOwnPropertySymbols</code>返回一个数组，包含对象自身的所有 Symbol 属性的键名。</p></section><section><h4>Reflect.ownKeys(obj)<a href="#reflectownkeysobj"><span>#</span></a></h4><p><code>Reflect.ownKeys</code>返回一个数组，包含对象自身的所有键名，不管键名是 Symbol 或字符串，也不管是否可枚举。</p><p>以上的 5 种方法遍历对象的键名，都遵守同样的属性遍历的次序规则。</p><ul>
<li>
<p>首先遍历所有数值键，按照数值升序排列。</p>
</li>
<li>
<p>其次遍历所有字符串键，按照加入时间升序排列。</p>
</li>
<li>
<p>最后遍历所有 Symbol 键，按照加入时间升序排列。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Reflect.ownKeys({ [Symbol()]: 0, b: 0, 10: 0, 2: 0, a: 0 })</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// ['2', '10', 'b', 'a', Symbol()]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>上面代码中，<code>Reflect.ownKeys</code>方法返回一个数组，包含了参数对象的所有属性。这个数组的属性次序是这样的，首先是数值属性<code>2</code>和<code>10</code>，其次是字符串属性<code>b</code>和<code>a</code>，最后是 <code>Symbol</code> 属性</p></section></section></section>
<section><h2>Object.getOwnPropertyDescriptors()<a href="#objectgetownpropertydescriptors"><span>#</span></a></h2><p>前面说过，<code>Object.getOwnPropertyDescriptor</code>方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了<code>Object.getOwnPropertyDescriptors</code>方法，返回指定对象所有自身属性(非继承属性)的描述对象</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>foo: 123,</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>get bar() {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>return 'abc'</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptors(obj)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// { foo:</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>//    { value: 123,</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>//      writable: true,</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>//      enumerable: true,</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>//      configurable: true },</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>//   bar:</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>//    { get: [Function: get bar],</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>//      set: undefined,</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>//      enumerable: true,</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>//      configurable: true } }</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>Object.getOwnPropertyDescriptors</code>方法返回一个对象，所有原对象的属性名都是该对象的属性名，对应的属性值就是该属性的描述对象</p><p>该方法的实现非常容易。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function getOwnPropertyDescriptors(obj) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>const result = {}</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>for (let key of Reflect.ownKeys(obj)) {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>result[key] = Object.getOwnPropertyDescriptor(obj, key)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>return result</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>该方法的引入目的，主要是为了解决<code>Object.assign()</code>无法正确拷贝<code>get</code>属性和<code>set</code>属性的问题</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const source = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>set foo(value) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>console.log(value)</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>const target1 = {}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Object.assign(target1, source)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptor(target1, 'foo')</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// { value: undefined,</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>//   writable: true,</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>//   enumerable: true,</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>//   configurable: true }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>source</code>对象的<code>foo</code>属性的值是一个赋值函数，<code>Object.assign</code>方法将这个属性拷贝给<code>target1</code>对象，结果该属性的值变成了<code>undefined</code>。这是因为<code>Object.assign方</code>法总是拷贝一个属性的值，而不会拷贝它背后的赋值方法或取值方法</p><p>这时，<code>Object.getOwnPropertyDescriptors</code>方法配合<code>Object.defineProperties</code>方法，就可以实现正确拷贝</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const source = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>set foo(value) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>console.log(value)</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>const target2 = {}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source))</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Object.getOwnPropertyDescriptor(target2, 'foo')</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// { get: undefined,</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>//   set: [Function: set foo],</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>//   enumerable: true,</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>//   configurable: true }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，两个对象合并的逻辑可以写成一个函数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const shallowMerge = (target, source) =&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Object.getOwnPropertyDescriptors</code>方法的另一个用处，是配合<code>Object.create</code>方法，将对象属性克隆到一个新对象。这属于浅拷贝</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const clone = Object.create(</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>Object.getPrototypeOf(obj),</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>Object.getOwnPropertyDescriptors(obj))</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 或者</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>const shallowClone = (obj) =&gt;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p>另外，<code>Object.getOwnPropertyDescriptors</code>方法可以实现一个对象继承另一个对象。以前，继承另一个对象，常常写成下面这样。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>__proto__: prot,</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>foo: 123,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>ES6 规定<code>__proto__</code>只有浏览器要部署，其他环境不用部署。如果去除<code>__proto__</code>，上面代码就要改成下面这样</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = Object.create(prot)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>obj.foo = 123</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 或者</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>const obj = Object.assign(Object.create(prot), {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>foo: 123,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>有了<code>Object.getOwnPropertyDescriptors</code>，我们就有了另一种写法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = Object.create(</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>prot,</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>Object.getOwnPropertyDescriptors({</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>foo: 123,</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Object.getOwnPropertyDescriptors</code>也可以用来实现 Mixin(混入)模式</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let mix = (object) =&gt; ({</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>with: (...mixins) =&gt;</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>mixins.reduce(</span></div></div><div><div><div>4</div></div><div><span><span>                </span></span><span>(c, mixin) =&gt; Object.create(c, Object.getOwnPropertyDescriptors(mixin)),</span></div></div><div><div><div>5</div></div><div><span><span>                </span></span><span>object</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>),</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// multiple mixins example</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>let a = { a: 'a' }</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>let b = { b: 'b' }</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>let c = { c: 'c' }</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>let d = mix(c).with(a, b)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>d.c // "c"</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>d.b // "b"</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>d.a // "a"</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码返回一个新的对象<code>d</code>，代表了对象<code>a</code>和<code>b</code>被混入了对象c的操作。</p><p>出于完整性的考虑，<code>Object.getOwnPropertyDescriptors</code>进入标准以后，以后还会新增<code>Reflect.getOwnPropertyDescriptors</code>方法</p></section>
<section><h2>ES6 中的隐式原型<a href="#es6-中的隐式原型"><span>#</span></a></h2><section><h3><code>__proto__</code>属性<a href="#__proto__属性"><span>#</span></a></h3><p><code>__proto__</code>属性(前后各两个下划线)，用来读取或设置当前对象的<code>prototype</code>对象。目前，所有浏览器(包括 IE11)都部署了这个属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// es5 的写法</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>method: function () {},</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>obj.__proto__ = someOtherObj</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// es6 的写法</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>var obj = Object.create(someOtherObj)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>obj.method = function () {}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>该属性没有写入 ES6 的正文，而是写入了附录，原因是<code>__proto__</code>前后的双下划线，说明它本质上是一个内部属性，而不是一个正式的对外的 API，只是由于浏览器广泛支持，才被加入了 ES6。</p><p>标准明确规定，只有浏览器必须部署这个属性，其他运行环境不一定需要部署，而且新的代码最好认为这个属性是不存在的。因此，无论从语义的角度，还是从兼容性的角度，都不要使用这个属性，而是使用下面的<code>Object.setPrototypeOf()</code>(写操作)、<code>Object.getPrototypeOf()</code>(读操作)、<code>Object.create()</code>(生成操作)代替。</p><p>实现上，<code>__proto__</code>调用的是<code>Object.prototype.__proto__</code>，具体实现如下。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.defineProperty(Object.prototype, '__proto__', {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>get() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>let _thisObj = Object(this)</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>return Object.getPrototypeOf(_thisObj)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>set(proto) {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>if (this === undefined || this === null) {</span></div></div><div><div><div>8</div></div><div><span><span>                </span></span><span>throw new TypeError()</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>if (!isObject(this)) {</span></div></div><div><div><div>11</div></div><div><span><span>                </span></span><span>return undefined</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>if (!isObject(proto)) {</span></div></div><div><div><div>14</div></div><div><span><span>                </span></span><span>return undefined</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>let status = Reflect.setPrototypeOf(this, proto)</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>if (!status) {</span></div></div><div><div><div>18</div></div><div><span><span>                </span></span><span>throw new TypeError()</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>function isObject(value) {</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>return Object(value) === value</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果一个对象本身部署了<code>__proto__</code>属性，该属性的值就是对象的原型。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object.getPrototypeOf({ __proto__: null })</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// null</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Object.create()<a href="#objectcreate"><span>#</span></a></h3><p><code>Object.create()</code>方法创建一个新对象，使用现有的对象来提供新创建的对象的<code>__proto__</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object.create(proto, [propertiesObject])</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Object.create()</code> ，可以理解为继承一个对象，添加的属性是在原型下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let a = { rep: 'apple' }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let b = Object.create(a)  //等价于 b.__proto__ = a</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console.log(b) // {}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log(b.__proto__) // {rep: "apple"}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console.log(b.rep) // {rep: "apple"}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>此时这个值不是<code>b</code>自身的，是它通过原型链<code>__proto__</code>来访问到<code>b</code>的值</p></section><section><h3>Object.setPrototypeOf()<a href="#objectsetprototypeof"><span>#</span></a></h3><p><code>Object.setPrototypeOf</code>方法的作用与<code>__proto__</code>相同，用来设置一个对象的<code>prototype</code>对象，返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 格式</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(object, prototype)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 用法</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>const o = Object.setPrototypeOf({}, null)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>该方法等同于下面的函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function setPrototypeOf(obj, proto) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>obj.__proto__ = proto</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>return obj</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>下面是一个例子。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let proto = {}</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let obj = { x: 10 }</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(obj, proto)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>proto.y = 20</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>proto.z = 40</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>obj.x // 10</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>obj.y // 20</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>obj.z // 40</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码将<code>proto</code>对象设为<code>obj</code>对象的原型，所以从<code>obj</code>对象可以读取<code>proto</code>对象的属性。</p><p>如果第一个参数不是对象，会自动转为对象。但是由于返回的还是第一个参数，所以这个操作不会产生任何效果</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(1, {}) === 1 // true</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf('foo', {}) === 'foo' // true</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(true, {}) === true // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>由于<code>undefined</code>和<code>null</code>无法转为对象，所以如果第一个参数是<code>undefined</code>或<code>null</code>，就会报错</p></section><section><h3>Object.getPrototypeOf()<a href="#objectgetprototypeof"><span>#</span></a></h3><p>该方法与<code>Object.setPrototypeOf</code>方法配套，用于读取一个对象的原型对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object.getPrototypeOf(obj)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>下面是一个例子。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function Rectangle() {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const rec = new Rectangle()</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf(rec) === Rectangle.prototype</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>Object.getPrototypeOf(rec) === rec.__proto__  //不推荐这种写法</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// true</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(rec, Object.prototype)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf(rec) === Rectangle.prototype // false</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>Object.getPrototypeOf(rec) === Object.prototype // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果参数不是对象，会被自动转为对象。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 等同于 Object.getPrototypeOf(Number(1))</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf(1)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// Number {[[PrimitiveValue]]: 0}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 等同于 Object.getPrototypeOf(String('foo'))</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf('foo')</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// String {length: 0, [[PrimitiveValue]]: ""}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>// 等同于 Object.getPrototypeOf(Boolean(true))</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf(true)</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// Boolean {[[PrimitiveValue]]: false}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf(1) === Number.prototype // true</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf('foo') === String.prototype // true</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>Object.getPrototypeOf(true) === Boolean.prototype // true</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果参数是<code>undefined</code>或<code>null</code>，它们无法转为对象，所以会报错</p></section></section>
<section><h2>super 关键字<a href="#super-关键字"><span>#</span></a></h2><p>我们知道，<code>this</code>关键字总是指向函数所在的当前对象，ES6 又新增了另一个类似的关键字<code>super</code>，指向当前对象的原型对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const proto = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>foo: 'hello',</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>foo: 'world',</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>find() {</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>return super.foo</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(obj, proto)</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>obj.find() // "hello"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，对象<code>obj</code>的<code>find</code>方法之中，通过<code>super.foo</code>引用了原型对象<code>proto</code>的<code>foo</code>属性。</p><p>注意，<code>super</code>关键字表示原型对象时，<strong>只能用在对象的方法之中</strong>，用在其他地方都会报错</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>foo: super.foo,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>foo: () =&gt; super.foo,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>foo: function () {</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>return super.foo</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面三种<code>super</code>的用法都会报错，因为对于 JavaScript 引擎来说，这里的super都没有用在对象的方法之中。
第一种写法是<code>super</code>用在属性里面，第二种和第三种写法是<code>super</code>用在一个函数里面，然后赋值给<code>foo</code>属性。目前，只有对象方法的简写法（<code>foo(){}</code>）可以让 JavaScript 引擎确认，定义的是对象的方法。</p><p>JavaScript 引擎内部，<code>super.foo</code>等同于<code>Object.getPrototypeOf(this).foo</code>(属性)或<code>Object.getPrototypeOf(this).foo.call(this)</code>(方法)。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const proto = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>x: 'hello',</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>foo() {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>console.log(this.x)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>x: 'world',</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>foo() {</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>super.foo()</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>Object.setPrototypeOf(obj, proto)</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>obj.foo() // "world"</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>super.foo</code>指向原型对象<code>proto</code>的<code>foo</code>方法，但是绑定的<code>this</code>却还是当前对象<code>obj</code>，因此输出的就是<code>world</code></p></section>
<section><h2>Object.keys()，Object.values()，Object.entries()<a href="#objectkeysobjectvaluesobjectentries"><span>#</span></a></h2><section><h3>Object.keys()<a href="#objectkeys"><span>#</span></a></h3><p>ES5 引入了<code>Object.keys</code>方法，返回一个数组，成员是参数对象自身的(不含继承的)所有可遍历(<code>enumerable</code>)属性的键名。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var obj = { foo: 'bar', baz: 42 }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.keys(obj)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// ["foo", "baz"]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>ES2017 引入了跟<code>Object.keys</code>配套的<code>Object.values</code>和<code>Object.entries</code>，作为遍历一个对象的补充手段，供<code>for...of</code>循环使用</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let { keys, values, entries } = Object</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let obj = { a: 1, b: 2, c: 3 }</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>for (let key of keys(obj)) {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>console.log(key) // 'a', 'b', 'c'</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>for (let value of values(obj)) {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console.log(value) // 1, 2, 3</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>for (let [key, value] of entries(obj)) {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>console.log([key, value]) // ['a', 1], ['b', 2], ['c', 3]</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Object.values()<a href="#objectvalues"><span>#</span></a></h3><p><code>Object.values</code>方法返回一个数组，成员是参数对象自身的(不含继承的)所有可遍历(<code>enumerable</code>)属性的键值，属性的顺序与通过手动循环对象的属性值所给出的顺序相同。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = { foo: 'bar', baz: 42 }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.values(obj)  // ["bar", 42]</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>var an_obj = { 100: 'a', 2: 'b', 7: 'c' }</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console.log(Object.values(an_obj)); // ['b', 'c', 'a']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注：</p><ol>
<li>
<p><code>Object.values</code>只返回对象自身的可遍历属性</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>const obj = Object.create({}, { p: { value: 42 } })</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>Object.values(obj) // []</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，<code>Object.create</code>方法的第二个参数添加的对象属性(属性<code>p</code>)，如果不显式声明，默认是不可遍历的，因为p的属性描述对象的<code>enumerable</code>默认是<code>false</code>，<code>Object.values</code>不会返回这个属性。只要把<code>enumerable</code>改成<code>true</code>，<code>Object.values</code>就会返回属性p的值
即：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>const obj = Object.create(</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>{},</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span><span>         </span></span><span>p: {</span></div></div><div><div><div>5</div></div><div><span><span>             </span></span><span>value: 42,</span></div></div><div><div><div>6</div></div><div><span><span>             </span></span><span>enumerable: true,</span></div></div><div><div><div>7</div></div><div><span><span>         </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span><span>     </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>)</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>Object.values(obj) // [42]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><code>Object.values</code>会过滤属性名为 Symbol 值的属性</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>Object.values({ [Symbol()]: 123, foo: 'abc' })</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>// ['abc']</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果<code>Object.values</code>方法的参数是一个字符串，会返回各个字符组成的一个数组</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>Object.values('foo')</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>// ['f', 'o', 'o']</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，字符串会先转成一个类似数组的对象。字符串的每个字符，就是该对象的一个属性。因此，Object.values返回每个属性的键值，就是各个字符组成的一个数组</p>
</li>
<li>
<p>如果参数不是对象，<code>Object.values</code>会先将其转为对象。由于数值和布尔值的包装对象，都不会为实例添加非继承的属性。所以，<code>Object.values</code>会返回空数组</p>
</li>
<li>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>Object.values(42) // []</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>Object.values(true) // []</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section><section><h3>Object.entries<a href="#objectentries"><span>#</span></a></h3><p><code>Object.entries</code>方法返回一个数组，成员是参数对象自身的(不含继承的)所有可遍历(<code>enumerable</code>)属性的键值对数组,除了返回值不一样，该方法的行为与<code>Object.values</code>基本一致</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = { foo: 'bar', baz: 42 }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Object.entries(obj)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// [ ["foo", "bar"], ["baz", 42] ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果原对象的属性名是一个 Symbol 值，该属性会被忽略，而<code>Reflect.ownEntries()</code>方法，返回对象自身的所有属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Object.entries({ [Symbol()]: 123, foo: 'abc' })</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// [ [ 'foo', 'abc' ] ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注：</p><ol>
<li>
<p><code>Object.entries</code>的基本用途是遍历对象的属性</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let obj = { one: 1, two: 2 }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>for (let [k, v] of Object.entries(obj)) {</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`)</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>// "one": 1</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>// "two": 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><code>Object.entries</code>方法的另一个用处是，将对象转为真正的Map结构</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>const obj = { foo: 'bar', baz: 42 }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>const map = new Map(Object.entries(obj))</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>map // Map { foo: "bar", baz: 42 }</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section></section>
<section><h2>对象的扩展运算符 <code>...</code><a href="#对象的扩展运算符-"><span>#</span></a></h2><section><h3>解构赋值<a href="#解构赋值"><span>#</span></a></h3><p>对象的解构赋值用于从一个对象取值，相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性，分配到指定的对象上面。所有的键和它们的值，都会拷贝到新对象上面</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x // 1</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>y // 2</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>z // { a: 3, b: 4 }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，变量<code>z</code>是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(<code>a</code>和<code>b</code>)，将它们连同值一起拷贝过来</p><p>注:</p><ol>
<li>
<p>由于解构赋值要求等号右边是一个对象，所以如果等号右边是<code>undefined</code>或<code>null</code>，就会报错，因为它们无法转为对象</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let { x, y, ...z } = null // 运行时错误</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let { x, y, ...z } = undefined // 运行时错误</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>解构赋值必须是最后一个参数，否则会报错</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let { ...x, y, z } = obj // 句法错误</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let { x, ...y, ...z } = obj // 句法错误</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>解构赋值的拷贝是<strong>浅拷贝</strong>，即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用，而不是这个值的副本</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let obj = { a: { b: 1 } }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let { ...x } = obj</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>obj.a.b = 2</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>x.a.b // 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>扩展运算符的解构赋值，不能复制继承自原型对象的属性。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let o1 = { a: 1 }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let o2 = { b: 2 }</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>o2.__proto__ = o1</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>let { ...o3 } = o2</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>o3 // { b: 2 }</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>o3.a // undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，对象<code>o3</code>复制了<code>o2</code>，但是只复制了<code>o2</code>自身的属性，没有复制它的原型对象<code>o1</code>的属性
例：
const o = Object.create({ x: 1, y: 2 })
o.z = 3</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let { x, ...newObj } = o</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let { y, z } = newObj</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>x // 1</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>y // undefined</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>z // 3</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，变量<code>x</code>是单纯的解构赋值，所以可以读取对象o继承的属性；
变量<code>y</code>和<code>z</code>是扩展运算符的解构赋值，只能读取对象<code>o</code>自身的属性，所以变量z可以赋值成功，变量y取不到值。</p>
<p>ES6 规定，变量声明语句之中，如果使用解构赋值，扩展运算符后面必须是一个变量名，而不能是一个解构赋值表达式，所以上面代码引入了中间变量<code>newObj</code>，如果写成下面这样会报错</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let { x, ...{ y, z } } = o;</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>// SyntaxError: ... must be followed by an identifier in declaration contexts</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>解构赋值的一个用处，是扩展某个函数的参数，引入其他操作</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>function baseFunction({ a, b }) {</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>// ...</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>function wrapperFunction({ x, y, ...restConfig }) {</span></div></div><div><div><div>5</div></div><div><span><span>     </span></span><span>// 使用 x 和 y 参数进行操作</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>// 其余参数传给原始函数</span></div></div><div><div><div>7</div></div><div><span><span>     </span></span><span>return baseFunction(restConfig)</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中，原始函数<code>baseFunction</code>接受<code>a</code>和<code>b</code>作为参数，函数<code>wrapperFunction</code>在<code>baseFunction</code>的基础上进行了扩展，能够接受多余的参数，并且保留原始函数的行为</p>
</li>
</ol></section><section><h3>扩展运算符<code>...</code><a href="#扩展运算符"><span>#</span></a></h3><p>对象的扩展运算符(<code>...</code>)用于取出参数对象的所有可遍历属性，拷贝到当前对象之中，这等同于使用<code>Object.assign</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let z = { a: 3, b: 4 }</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let n = { ...z }</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>n // { a: 3, b: 4 }</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>// 等同于</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>let n2 = Object.assign({}, z)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的例子只是拷贝了对象实例的属性，如果想完整克隆一个对象，还拷贝对象原型的属性，可以采用下面的写法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 写法一 （不推荐）</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const clone1 = {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>__proto__: Object.getPrototypeOf(obj),</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>...obj,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// 写法二</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>const clone2 = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// 写法三</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>const clone3 = Object.create(</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>Object.getPrototypeOf(obj),</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>Object.getOwnPropertyDescriptors(obj)</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注：</p><ol>
<li>
<p>扩展运算符可以用于合并两个对象</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let ab = { ...a, ...b }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>// 等同于</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>let ab2 = Object.assign({}, a, b)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果用户自定义的属性，放在扩展运算符后面，则扩展运算符内部的同名属性会被覆盖掉</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let a = { x: 'x', y: 'y', z: 'z' }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let aWithOverrides = { ...a, x: 1, y: 2 }</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>// 等同于</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>let aWithOverrides = { ...a, ...{ x: 1, y: 2 } }</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>let x = 1,</span></div></div><div><div><div>7</div></div><div><span><span>     </span></span><span>y = 2,</span></div></div><div><div><div>8</div></div><div><span><span>     </span></span><span>aWithOverrides = { ...a, x, y }</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>// 等同于</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 })</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>// {x: 1, y: 2, z: 'z'}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果把自定义属性放在扩展运算符前面，就变成了设置新对象的默认属性值</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let a = { x: 'x', z: 'z' }</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let aWithDefaults = { x: 1, y: 2, ...a }</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>// 等同于</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>let aWithDefaults2 = Object.assign({}, { x: 1, y: 2 }, a)</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>// 等同于</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>let aWithDefaults3 = Object.assign({ x: 1, y: 2 }, a)</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>// {x: 'x', y: 2, z: 'z'}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>与数组的扩展运算符一样，对象的扩展运算符后面可以跟表达式</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let x = 2</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>const obj = {</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>...(x &gt; 1 ? { a: 1 } : {}),</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>b: 2,</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>// {a: 1, b: 2}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果扩展运算符后面是一个空对象，则没有任何效果。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>{...{}, a: 1}</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>// { a: 1 }</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果扩展运算符的参数是null或undefined，这两个值会被忽略，不会报错</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let emptyObject = { ...null, ...undefined } // {} 不报错</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>扩展运算符的参数对象之中，如果有取值函数<code>get</code>，这个函数是会执行的。</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>// 并不会抛出错误，因为 x 属性只是被定义，但没执行</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>let aWithXGetter = {</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>...a,</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>get x() {</span></div></div><div><div><div>5</div></div><div><span><span>         </span></span><span>throw new Error('not throw yet')</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span> </span></span><span>// 会抛出错误，因为 x 属性被执行了</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>let runtimeError = {</span></div></div><div><div><div>11</div></div><div><span><span>     </span></span><span>...a,</span></div></div><div><div><div>12</div></div><div><span><span>     </span></span><span>...{</span></div></div><div><div><div>13</div></div><div><span><span>         </span></span><span>get x() {</span></div></div><div><div><div>14</div></div><div><span><span>             </span></span><span>throw new Error('throw now')</span></div></div><div><div><div>15</div></div><div><span><span>         </span></span><span>},</span></div></div><div><div><div>16</div></div><div><span><span>     </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/class-de-ji-cheng/</id>
      <title type="text">Class的继承</title>
      <published>2022-06-26T00:22:19.000Z</published>
      <updated>2022-06-26T00:22:19.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/class-de-ji-cheng/"/>
      <summary type="text">Class 可以通过 extends 关键字实现继承，这比 ES5 的通过修改原型链实现继承，要清晰和方便很多。</summary>
      <content type="html"><![CDATA[<p>Class 可以通过<code>extends</code>关键字实现继承，这比 ES5 的通过修改原型链实现继承，要清晰和方便很多。</p>
<section><h2>extends关键字<a href="#extends关键字"><span>#</span></a></h2><p>Class 可以通过<code>extends</code>关键字实现继承</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span><span>(</span><span>x</span><span>, </span><span>y</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>            </span><span>this</span><span>.</span><span>y</span><span> </span><span>=</span><span><span> </span><span>y</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>        </span><span>toString</span><span>() {</span></div></div><div><div><div>8</div></div><div><span>            </span><span>return</span><span> </span><span>"("</span><span> </span><span>+</span><span> </span><span>this</span><span>.</span><span>x</span><span> </span><span>+</span><span> </span><span>", "</span><span> </span><span>+</span><span> </span><span>this</span><span>.</span><span>y</span><span> </span><span>+</span><span> </span><span>")"</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>class</span><span> </span><span>ColorPoint</span><span> </span><span>extends</span><span> </span><span>Point</span><span> {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>constructor</span><span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>color</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>            </span><span>super</span><span><span>(</span><span>x</span><span>, </span><span>y</span><span>); </span></span><span>// 调用父类的constructor(x, y)</span></div></div><div><div><div>15</div></div><div><span>            </span><span>this</span><span>.</span><span>color</span><span> </span><span>=</span><span><span> </span><span>color</span><span>;</span></span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>        </span><span>toString</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>            </span><span>return</span><span> </span><span>this</span><span>.</span><span>color</span><span> </span><span>+</span><span> </span><span>" "</span><span> </span><span>+</span><span> </span><span>super</span><span>.</span><span>toString</span><span>(); </span><span>// 调用父类的toString()</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码定义了一个<code>ColorPoint</code>类，该类通过<code>extends</code>关键字，继承了<code>Point</code>类的所有属性和方法。
<code>constructor</code>方法和<code>toString</code>方法之中，都出现了<code>super</code>关键字，它在这里表示父类的构造函数，用来新建父类的<code>this</code>对象。</p><p>子类必须在<code>constructor</code>方法中调用<code>super</code>方法，否则新建实例时会报错。这是因为子类自己的<code>this</code>对象，必须先通过父类的构造函数完成塑造，得到与父类同样的实例属性和方法，然后再对其进行加工，加上子类自己的实例属性和方法。如果不调用<code>super</code>方法，子类就得不到<code>this</code>对象。
另一个需要注意的地方是，在子类的构造函数中，只有调用<code>super</code>之后，才可以使用<code>this</code>关键字，否则会报错。这是因为子类实例的构建，基于父类实例，只有<code>super</code>方法才能调用父类实例。</p></section>
<section><h2>Object.getPrototypeOf()<a href="#objectgetprototypeof"><span>#</span></a></h2><p><code>Object.getPrototypeOf</code>方法可以用来从子类上获取父类。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Object</span><span>.</span><span>getPrototypeOf</span><span><span>(</span><span>ColorPoint</span><span>) </span></span><span>===</span><span><span> </span><span>Point</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因此，可以使用这个方法判断，一个类是否继承了另一个类。</p></section>
<section><h2>super<a href="#super"><span>#</span></a></h2><p><code>super</code>这个关键字，既可以当作函数使用，也可以当作对象使用。在这两种情况下，它的用法完全不同。</p><section><h3>作为函数调用<a href="#作为函数调用"><span>#</span></a></h3><p><code>super</code>作为函数调用时，代表父类的构造函数。ES6 要求，子类的构造函数必须执行一次<code>super</code>函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>            </span><span>super</span><span>()</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>子类<code>B</code>的构造函数之中的<code>super()</code>，代表调用父类的构造函数。这是必须的，否则 JavaScript 引擎会报错</p><p><code>super</code>虽然代表了父类<code>A</code>的构造函数，但是返回的是子类B的实例，即s<code>uper</code>内部的<code>this</code>指的是<code>B</code>，因此<code>super()</code>在这里相当于<code>A.prototype.constructor.call(this)</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>new</span><span>.</span><span>target</span><span>.</span><span>name</span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>8</div></div><div><span>            </span><span>super</span><span>();</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>new</span><span> </span><span>A</span><span>(); </span><span>// A</span></div></div><div><div><div>12</div></div><div><span>    </span><span>new</span><span> </span><span>B</span><span>(); </span><span>// B</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>new.target</code>指向当前正在执行的函数。可以看到，在<code>super()</code>执行时，它指向的是子类B的构造函数，而不是父类A的构造函数。也就是说，<code>super()</code>内部的<code>this</code>指向的是B。</p><p>作为函数时，<code>super()</code>只能用在子类的构造函数之中，用在其他地方就会报错</p><p>###作为对象使用 ###
<code>super</code>作为对象时，在普通方法中，指向父类的原型对象；在静态方法中，指向父类</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>p</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>return</span><span> </span><span>2</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>            </span><span>super</span><span>()</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>super</span><span>.</span><span>p</span><span>()) </span><span>// 2</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>let</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>B</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，子类B当中的<code>super.p()</code>，就是将<code>super</code>当作一个对象使用。这时，<code>super</code>在普通方法之中，指向<code>A.prototype</code>，所以<code>super.p()</code>就相当于<code>A.prototype.p()</code>。
这里需要注意，由于<code>super</code>指向父类的原型对象，所以定义在父类实例上的方法或属性，是无法通过<code>super</code>调用的，而定义在父类原型对象上的，<code>super</code> 就可以直接取到</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>this</span><span>.</span><span>p</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>A</span><span>.</span><span>prototype</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>p</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>            </span><span>return</span><span> </span><span>super</span><span>.</span><span>p</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>        </span><span>x</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>            </span><span>return</span><span> </span><span>super</span><span>.</span><span>x</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>let</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>B</span><span>()</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>b</span><span>.</span><span>p</span><span>() </span><span>// undefined</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>b</span><span>.</span><span>x</span><span>() </span><span>// 2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>ES6 规定，在子类普通方法中通过<code>super</code>调用父类的方法时，方法内部的<code>this</code>指向当前的子类实例。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>        </span><span>print</span><span>() {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>x</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>            </span><span>super</span><span>()</span></div></div><div><div><div>13</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>        </span><span>m</span><span>() {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>super</span><span>.</span><span>print</span><span>()</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>let</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>B</span><span>()</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>b</span><span>.</span><span>m</span><span>() </span><span>// 2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>super.print()</code>虽然调用的是<code>A.prototype.print()</code>，但是<code>A.prototype.print()</code>内部的<code>this</code>指向子类B的实例，导致输出的是2，而不是1。也就是说，实际上执行的是<code>super.print.call(this)</code>。</p><p>由于this指向子类实例，所以如果通过super对某个属性赋值，这时super就是this，赋值的属性会变成子类实例的属性。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>            </span><span>super</span><span>()</span></div></div><div><div><div>10</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>11</div></div><div><span>            </span><span>super</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>super</span><span>.</span><span>x</span><span>) </span><span>// undefined</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>x</span><span>) </span><span>// 3</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>let</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>B</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>super.x</code>赋值为3，这时等同于对<code>this.x</code>赋值为3。而当读取<code>super.x</code>的时候，读的是<code>A.prototype.x</code>，所以返回<code>undefined</code>。</p><p>如果<code>super</code>作为对象，用在静态方法之中，这时<code>super</code>将指向父类，而不是父类的原型对象。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>Parent</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>static</span><span> </span><span>myMethod</span><span><span>(</span><span>msg</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'static'</span><span><span>, </span><span>msg</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>        </span><span>myMethod</span><span><span>(</span><span>msg</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'instance'</span><span><span>, </span><span>msg</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>class</span><span> </span><span>Child</span><span> </span><span>extends</span><span> </span><span>Parent</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>static</span><span> </span><span>myMethod</span><span><span>(</span><span>msg</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span>            </span><span>super</span><span>.</span><span>myMethod</span><span><span>(</span><span>msg</span><span>)</span></span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>        </span><span>myMethod</span><span><span>(</span><span>msg</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>            </span><span>super</span><span>.</span><span>myMethod</span><span><span>(</span><span>msg</span><span>)</span></span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>Child</span><span>.</span><span>myMethod</span><span>(</span><span>1</span><span>) </span><span>// static 1</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>let</span><span><span> </span><span>child</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Child</span><span>()</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>child</span><span>.</span><span>myMethod</span><span>(</span><span>2</span><span>) </span><span>// instance 2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>super</code>在静态方法之中指向父类，在普通方法之中指向父类的原型对象。</p><p>另外，在子类的静态方法中通过<code>super</code>调用父类的方法时，方法内部的<code>this</code>指向当前的子类，而不是子类的实例。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span>        </span><span>static</span><span> </span><span>print</span><span>() {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>x</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>            </span><span>super</span><span>()</span></div></div><div><div><div>13</div></div><div><span>            </span><span>this</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>        </span><span>static</span><span> </span><span>m</span><span>() {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>super</span><span>.</span><span>print</span><span>()</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>        </span><span>m</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>            </span><span>super</span><span>.</span><span>print</span><span>()</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>B</span><span>.</span><span>x</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>24</div></div><div><span>    </span><span>B</span><span>.</span><span>m</span><span>() </span><span>// 3</span></div></div><div><div><div>25</div></div><div><span>    </span><span>new</span><span> </span><span>B</span><span>().</span><span>m</span><span>() </span><span>//TypeError: (intermediate value).print is not a function</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>静态方法<code>B.m</code>里面，<code>super.print</code>指向父类的静态方法。这个方法里面的<code>this</code>指向的是<code>B</code>，而不是<code>B</code>的实例，而普通方法<code>B.m()</code>中，由于<code>super</code>指向父类的原型对象<code>A.prototype</code>，其中没有<code> print()</code>这个方法，所以会报错</p><p>最后，由于对象总是继承其他对象的，所以可以在任意一个对象中，使用<code>super</code>关键字</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>obj</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>toString</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>return</span><span> </span><span>"MyObject: "</span><span> </span><span>+</span><span> </span><span>super</span><span>.</span><span>toString</span><span>()</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>obj</span><span>.</span><span>toString</span><span>() </span><span>// MyObject: [object Object]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>类的 <code>prototype</code> 属性和<code>__proto__</code>属性<a href="#类的-prototype-属性和__proto__属性"><span>#</span></a></h2><p>大多数浏览器的 ES5 实现之中，每一个对象都有<code>__proto__</code>属性，指向对应的构造函数的<code>prototype</code>属性。Class 作为构造函数的语法糖，同时有<code>prototype</code>属性和<code>__proto__</code>属性，因此同时存在两条继承链。</p><ol>
<li>
<p>子类的<code>__proto__</code>属性，表示构造函数的继承，总是指向父类。</p>
</li>
<li>
<p>子类<code>prototype</code>属性的<code>__proto__</code>属性，表示方法的继承，总是指向父类的<code>prototype</code>属 性。</p>
</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>A</span><span> {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>B</span><span>.</span><span>__proto__</span><span> </span><span>===</span><span> </span><span>A</span><span> </span><span>// true</span></div></div><div><div><div>6</div></div><div><span>B</span><span>.</span><span>prototype</span><span>.</span><span>__proto__</span><span> </span><span>===</span><span> </span><span>A</span><span>.</span><span>prototype</span><span> </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样的结果是因为，类的继承是按照下面的模式实现的:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>class</span><span> </span><span>A</span><span> {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>  </span><span>class</span><span> </span><span>B</span><span> {}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>// B 的实例继承 A 的实例</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>Object</span><span>.</span><span>setPrototypeOf</span><span>(</span><span>B</span><span>.</span><span>prototype</span><span>, </span><span>A</span><span>.</span><span>prototype</span><span>)</span></div></div><div><div><div>7</div></div><div><span>  </span><span>// 等同于B.prototype.__proto__ = A.prototype</span></div></div><div><div><div>8</div></div><div><span>  </span><span>// B 继承 A 的静态属性</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>Object</span><span>.</span><span>setPrototypeOf</span><span>(</span><span>B</span><span>, </span><span>A</span><span>)</span></div></div><div><div><div>11</div></div><div><span>  </span><span>// 等同于B.__proto__ = A</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>  </span><span>const</span><span> </span><span>b</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>B</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这两条继承链，可以这样理解: 作为一个对象，子类(B)的原型(<code>__proto__</code>属性)是父类(A)；作为一个构造函数，子类(B)的原型对象(<code>prototype</code>属性)是父类的原型对象(<code>prototype</code>属性)的实例</p></section>
<section><h2>实例的<code> __proto__</code> 属性<a href="#实例的-__proto__-属性"><span>#</span></a></h2><p>子类实例的<code>__proto__</code>属性的<code>__proto__</code>属性，指向父类实例的<code>__proto__</code>属性。也就是说，子类的原型的原型，是父类的原型</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>A</span><span> {}</span></div></div><div><div><div>2</div></div><div><span>    </span><span>class</span><span> </span><span>B</span><span> </span><span>extends</span><span> </span><span>A</span><span> {}</span></div></div><div><div><div>3</div></div><div><span>    </span><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>A</span><span>()</span></div></div><div><div><div>4</div></div><div><span>    </span><span>let</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>B</span><span>()</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>b</span><span>.</span><span>__proto__</span><span> </span><span>===</span><span> </span><span>B</span><span>.</span><span>prototype</span><span> </span><span>//true</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>b</span><span>.</span><span>__proto__</span><span>.</span><span>__proto__</span><span> </span><span>===</span><span><span> </span><span>a</span><span>.</span></span><span>__proto__</span><span> </span><span>//true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因此，通过子类实例的<code>__proto__.__proto__</code>属性，可以修改父类实例的行为</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>b</span><span>.</span><span>__proto__</span><span>.</span><span>__proto__</span><span>.</span><span>fun</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"fun"</span><span>)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>a</span><span>.</span><span>fun</span><span>() </span><span>// "fun"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>原生构造函数的继承<a href="#原生构造函数的继承"><span>#</span></a></h2><p>原生构造函数是指语言内置的构造函数，通常用来生成数据结构。ECMAScript 的原生构造函数大致有下面这些。</p><ul>
<li><code>Boolean()</code></li>
<li><code>Number()</code></li>
<li><code>String()</code></li>
<li><code>Array()</code></li>
<li><code>Date()</code></li>
<li><code>Function()</code></li>
<li><code>RegExp()</code></li>
<li><code>Error()</code></li>
<li><code>Object()</code></li>
</ul><p>以前，这些原生构造函数是无法继承的，比如，不能自己定义一个<code>Array</code>的子类。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>MyArray</span><span>() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>Array</span><span>.</span><span>apply</span><span>(</span><span>this</span><span>, </span><span>arguments</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>MyArray</span><span>.</span><span>prototype</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>create</span><span>(</span><span>Array</span><span>.</span><span>prototype</span><span>, {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>constructor</span><span>:</span><span> {</span></div></div><div><div><div>7</div></div><div><span>            </span><span>value</span><span>:</span><span><span> </span><span>MyArray</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span>            </span><span>writable</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>9</div></div><div><span>            </span><span>configurable</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>10</div></div><div><span>            </span><span>enumerable</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码定义了一个继承 <code>Array</code> 的<code>MyArray</code>类。但是，这个类的行为与<code>Array</code>完全不一致。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>colors</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>MyArray</span><span>()</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>colors</span><span>[</span><span>0</span><span>] </span><span>=</span><span> </span><span>'red'</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>colors</span><span>.</span><span>length</span><span> </span><span>// 0</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>colors</span><span>.</span><span>length</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>colors</span><span>[</span><span>0</span><span>] </span><span>// "red"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>之所以会发生这种情况，是因为子类无法获得原生构造函数的内部属性，通过<code>Array.apply()</code>或者分配给原型对象都不行。原生构造函数会忽略<code>apply</code>方法传入的<code>this</code>，也就是说，原生构造函数的<code>this</code>无法绑定，导致拿不到内部属性。</p><p>ES5 是先新建子类的实例对象<code>this</code>，再将父类的属性添加到子类上，由于父类的内部属性无法获取，导致无法继承原生的构造函数。比如，<code>Array</code>构造函数有一个内部属性<code>[[DefineOwnProperty]]</code>，用来定义新属性时，更新<code>length</code>属性，这个内部属性无法在子类获取，导致子类的<code>length</code>属性行为不正常。</p><p>下面的例子中，我们想让一个普通对象继承<code>Error</code>对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>e</span><span> </span></span><span>=</span><span> {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Object</span><span>.</span><span>getOwnPropertyNames</span><span><span>(</span><span>Error</span><span>.</span></span><span>call</span><span><span>(</span><span>e</span><span>))</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>// [ 'stack' ]</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>Object</span><span>.</span><span>getOwnPropertyNames</span><span><span>(</span><span>e</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>// []</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，我们想通过<code>Error.call(e)</code>这种写法，让普通对象e具有<code>Error</code>对象的实例属性。但是，<code>Error.call()</code>完全忽略传入的第一个参数，而是返回一个新对象，<code>e</code>本身没有任何变化。这证明了<code>Error.call(e)</code>这种写法，无法继承原生构造函数。</p><p>ES6 允许继承原生构造函数定义子类，因为 ES6 是先新建父类的实例对象<code>this</code>，然后再用子类的构造函数修饰<code>this</code>，使得父类的所有行为都可以继承。</p><p>下面是ES6中一个继承<code>Array</code>的例子。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>MyArray</span><span> </span><span>extends</span><span> </span><span>Array</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>(</span><span>...</span><span><span>args</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>            </span><span>super</span><span>(</span><span>...</span><span><span>args</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>let</span><span><span> </span><span>arr</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>MyArray</span><span>();</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>arr</span><span>[</span><span>0</span><span>] </span><span>=</span><span> </span><span>12</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>arr</span><span>.</span><span>length</span><span> </span><span>// 1</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>arr</span><span>.</span><span>length</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>arr</span><span>[</span><span>0</span><span>] </span><span>// undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码定义了一个<code>MyArray</code>类，继承了<code>Array</code>构造函数，因此就可以从<code>MyArray</code>生成数组的实例。这意味着，ES6 可以自定义原生数据结构(比如<code>Array</code>、<code>String</code>等)的子类，这是 ES5 无法做到的。</p><p>上面这个例子也说明，<code>extends</code>关键字不仅可以用来继承类，还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上，定义自己的数据结构。</p><p>下面就是定义了一个带版本功能的数组。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>class</span><span> </span><span>VersionedArray</span><span> </span><span>extends</span><span> </span><span>Array</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>            </span><span>super</span><span>()</span></div></div><div><div><div>4</div></div><div><span>            </span><span>this</span><span>.</span><span>history</span><span> </span><span>=</span><span> [[]]</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>        </span><span>commit</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>            </span><span>this</span><span>.</span><span>history</span><span>.</span><span>push</span><span>(</span><span>this</span><span>.</span><span>slice</span><span>())</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>        </span><span>revert</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>            </span><span>this</span><span>.</span><span>splice</span><span>(</span><span>0</span><span>, </span><span>this</span><span>.</span><span>length</span><span>, </span><span>...</span><span>this</span><span>.</span><span>history</span><span>[</span><span>this</span><span>.</span><span>history</span><span>.</span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span>])</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>let</span><span><span> </span><span>x</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>VersionedArray</span><span>()</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>push</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>push</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>x</span><span> </span><span>// [1, 2]</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>history</span><span> </span><span>// [[]]</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>commit</span><span>()</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>history</span><span> </span><span>// [[], [1, 2]]</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>push</span><span>(</span><span>3</span><span>)</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>x</span><span> </span><span>// [1, 2, 3]</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>history</span><span> </span><span>// [[], [1, 2]]</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>x</span><span>.</span><span>revert</span><span>()</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>x</span><span> </span><span>// [1, 2]</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>VersionedArray</code>会通过<code>commit</code>方法，将自己的当前状态生成一个版本快照，存入<code>history</code>属性。<code>revert</code>方法用来将数组重置为最新一次保存的版本。除此之外，<code>VersionedArray</code>依然是一个普通数组，所有原生的数组方法都可以在它上面调用。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/class-de-ji-ben-yong-fa/</id>
      <title type="text">Class的基本用法</title>
      <published>2022-06-25T00:11:40.000Z</published>
      <updated>2022-06-25T00:11:40.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/class-de-ji-ben-yong-fa/"/>
      <summary type="text">ES6 提供了更接近传统语言的写法，引入了 Class(类)这个概念，作为对象的模板。通过class关键字，可以定义类。 基本上，ES6 的class可以看作只是一个语法糖，它的绝大部分功能，ES5 都可以做到，新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。</summary>
      <content type="html"><![CDATA[<p>ES6 提供了更接近传统语言的写法，引入了 Class(类)这个概念，作为对象的模板。通过class关键字，可以定义类。
基本上，ES6 的class可以看作只是一个语法糖，它的绝大部分功能，ES5 都可以做到，新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。</p>
<section><h2>简介<a href="#简介"><span>#</span></a></h2><p>JavaScript 语言中，生成实例对象的传统方法是通过构造函数。下面是一个例子。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function Obj(x, y) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>this.x = x</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>this.y = y</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Obj.prototype.add = function () {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>return this.x + this.y</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>let num = new Obj(1, 2)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的代码用 ES6 的class改写，就是下面这样</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Obj {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>constructor(x, y){</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>this.x = x</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>this.y = y</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>add() {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>return this.x + this.y</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码定义了一个“类”，可以看到里面有一个constructor方法，这就是构造方法，而this关键字则代表实例对象</p><p><code>obj</code> 类除了构造方法，还定义了一个 <code>add()</code> 方法。注意，定义“类”的方法的时候，前面不需要加上<code>function</code>这个关键字，直接把函数定义放进去了就可以了。另外，方法之间不需要逗号分隔，加了会报错。</p><p>ES6 的类，完全可以看作构造函数的另一种写法,类的数据类型就是函数，类本身就指向构造函数。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>class Obj {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// ...</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>typeof Obj // "function"</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>Obj === Obj.prototype.constructor // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>构造函数的 <code>prototype</code> 属性，在 ES6 的“类”上面继续存在。事实上，类的所有方法都定义在类的 <code>prototype</code> 属性上面</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Obj {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>constructor(){  }</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>add() {  }</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>//等同于</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>Obj.prototype = {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>constructor(){  },</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>add() {  }</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因此在类的实例上调用方法。相当于调用原型上的方法。</p></section>
<section><h2>constructor方法<a href="#constructor方法"><span>#</span></a></h2><p><code>constructor</code>方法是类的默认方法，通过new命令生成对象实例时，自动调用该方法。一个类必须有<code>constructor</code>方法，如果没有显式定义，一个空的<code>constructor</code>方法会被默认添加</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>class Obj {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>// 等同于</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>class Obj {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>constructor() {}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，定义了一个空的类 <code>Obj</code>，JavaScript 引擎会自动为它添加一个空的<code>constructor</code>方法。 <code>constructor</code>方法默认返回实例对象(即<code>this</code>)，完全可以指定返回另外一个对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Foo {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>constructor(){</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>return {}</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>new Foo() instanceof Foo  // flase</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>constructor</code> 函数返回一个全新的对象，结果导致实例对象不是Foo类的实例</p><p><strong>注</strong>：类必须使用<code>new</code>调用，否则会报错。这是它跟普通构造函数的一个主要区别，构造函数不用<code>new</code>也可以执行。</p></section>
<section><h2>new操作符<a href="#new操作符"><span>#</span></a></h2><p>众所周知，在JS中，new的作用是通过构造函数来创建一个实例对象</p><p>实际上，<code>new</code> 操作符帮我们做了这些事</p><ol>
<li>
<p>帮我们创建了一个空对象，例如：obj；</p>
</li>
<li>
<p>将空对象原型的内存地址__proto__指向函数的原型对象；</p>
</li>
<li>
<p>利用函数的call方法，将原本指向window的绑定对象this指向了obj。（这样一来，当我们向函数中再传递实参时，对象的属性就会被挂载到obj上。）</p>
</li>
<li>
<p>利用函数返回对象obj。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>function Foo(name) {</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>this.name = name</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>let foo = new Foo("aaa")</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span> </span></span><span>//等价于</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>function Foo(name) {</span></div></div><div><div><div>8</div></div><div><span><span>     </span></span><span>this.name = name</span></div></div><div><div><div>9</div></div><div><span><span>     </span></span><span>return this          //4</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>let obj = {}           //1</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>obj.__proto__ = Foo.prototype  //2</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>// Foo.call(obj, 'aaa')</span></div></div><div><div><div>14</div></div><div><span><span> </span></span><span>let foo = Foo.call(obj, 'aaa') //3</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol><p><strong>注</strong>：类的构造函数与普通构造函数之间的区别是，调用构造函数必须使用<code>new</code> 操作符。而普通构造函数如果不使用 <code>new</code> 调用，name就会以全局的 <code>this</code> （通常是 <code>window</code>）作为内部对象。调用类构造函数时如果忘记使用 <code>new</code> 则会报错。</p></section>
<section><h2>类的实例对象<a href="#类的实例对象"><span>#</span></a></h2><p>与 ES5 一样，实例的属性除非显式定义在其本身(即定义在this对象上)，否则都是定义在原型上(即定义在class上)。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>//定义类</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>class Point {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>constructor(x, y) {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>this.x = x</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>this.y = y</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>toString() {</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>return "(" + this.x + ", " + this.y + ")"</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>let point = new Point(2, 3)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>point.toString() // (2, 3)</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>point.hasOwnProperty("x") // true</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>point.hasOwnProperty("y") // true</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>point.hasOwnProperty("toString") // false</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>point.__proto__.hasOwnProperty("toString") // true</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>x</code>和<code>y</code>都是实例对象<code>point</code>自身的属性(因为定义在<code>this</code>变量上)，所以<code>hasOwnProperty</code>方法返回<code>true</code>，而<code>toString</code>是原型对象的属性(因为定义在<code>Point</code>类上)，所以<code>hasOwnProperty</code>方法返回<code>false</code>。这些都与 ES5 的行为保持一致。</p><p>与 ES5 一样，类的所有实例共享一个原型对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let p1 = new Point(2, 3)</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let p2 = new Point(3, 2)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>p1.__proto__ === p2.__proto__  //true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>p1</code>和<code>p2</code>都是<code>Point</code>的实例，它们的原型都是P<code>oint.prototype</code>，所以<code>__proto__</code>属性是相等的。</p><p>这也意味着，可以通过实例的<code>__proto__</code>属性为“类”添加方法。</p><blockquote><p><strong>proto</strong> 并不是语言本身的特性，这是各大厂商具体实现时添加的私有属性，虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性，但依旧不建议在生产中使用该属性，避免对环境产生依赖。生产环境中，我们可以使用 <code>Object.getPrototypeOf</code> 方法来获取实例对象的原型，然后再来为原型添加方法/属性。</p></blockquote></section>
<section><h2>Class表达式<a href="#class表达式"><span>#</span></a></h2><p>与函数一样，类也可以使用表达式的形式定义</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const MyClass = class Me {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>getClassName() {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>return Me.name</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码使用表达式定义了一个类。需要注意的是，这个类的名字是<code>MyClass</code>而不是<code>Me</code>，<code>Me</code>只在 Class 的内部代码可用，指代当前类。如果类的内部没用到的话，可以省略 <code>Me</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let inst = new MyClass()</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>inst.getClassName() // Me</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Me.name // ReferenceError: Me is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>采用 Class 表达式，可以写出立即执行的 Class。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let person = new (class {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>constructor(name) {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>this.name = name</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>sayName() {</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>console.log(this.name)</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>})("张三")</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>person.sayName() // "张三"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Class 的取值函数(getter)和存值函数(setter)<a href="#class-的取值函数getter和存值函数setter"><span>#</span></a></h2><p>与 ES5 一样，在“类”的内部可以使用<code>get</code>和<code>set</code>关键字，对某个属性设置存值函数和取值函数，拦截该属性的存取行为。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class MyClass {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>constructor() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>// ...</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>get prop() {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>return "getter"</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>set prop(value) {</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>console.log("setter: " + value)</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>let inst = new MyClass()</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>inst.prop = 123  // setter: 123</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>inst.prop  // 'getter'</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>prop</code>属性有对应的存值函数和取值函数，因此赋值和读取行为都被自定义了。</p><p>存值函数和取值函数是设置在属性的 <code>Descriptor</code> 对象上的</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>class CustomHTMLElement {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>constructor(element) {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>this.element = element</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>get html() {</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>return this.element.innerHTML</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>set html(value) {</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>this.element.innerHTML = value</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>let descriptor = Object.getOwnPropertyDescriptor(</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>CustomHTMLElement.prototype,</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>"html"</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>"get" in descriptor // true</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>"set" in descriptor // true</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，存值函数和取值函数是定义在<code>html</code>属性的描述对象上面，这与 ES5 完全一致</p></section>
<section><h2>Class 的 Generator 方法<a href="#class-的-generator-方法"><span>#</span></a></h2><p>如果某个方法之前加上星号(*)，就表示该方法是一个 Generator 函数。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>class Foo {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>constructor(...args) {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>this.args = args;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>*[Symbol.iterator]() {</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>for (let arg of this.args) {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>yield arg;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>for (let x of new Foo("hello", "world")) {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>console.log(x);</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>// hello</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>// world</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>Foo</code>类的<code>Symbol.iterator</code>方法前有一个星号，表示该方法是一个 <code>Generator</code> 函数。
<code>Symbol.iterator</code>方法返回一个<code>Foo</code>类的默认遍历器，<code>for...of</code>循环会自动调用这个遍历器</p></section>
<section><h2>Class 的静态方法<a href="#class-的静态方法"><span>#</span></a></h2><p>类相当于实例的原型，所有在类中定义的方法，都会被实例继承。如果在一个方法前，加上<code>static</code>关键字，就表示该方法不会被实例继承，而是直接通过类来调用，这就称为“静态方法”。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>class Foo {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>static classMethod() {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>return "hello"</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>Foo.classMethod() // 'hello'</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>let foo = new Foo()</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>foo.classMethod() // TypeError: foo.classMethod is not a function</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Foo</code>类的<code>classMethod</code>方法前有<code>static</code>关键字，表明该方法是一个静态方法，可以直接在<code>Foo</code>类上调用<code>Foo.classMethod()</code>，而不是在<code>Foo</code>类的实例上调用。如果在实例上调用静态方法，会抛出一个错误，表示不存在该方法</p><p>注意，如果静态方法包含this关键字，这个this指的是类，而不是实例。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Foo {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>static bar() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>this.baz()</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>static baz() {</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>console.log("hello")</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>baz() {</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>console.log("world")</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>Foo.bar() // hello</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>静态方法<code>bar</code>调用了<code>this.baz</code>，这里的<code>this</code>指的是<code>Foo</code>类，而不是<code>Foo</code>的实例，等同于调用<code>Foo.baz</code>。另外，从这个例子还可以看出，静态方法可以与非静态方法重名</p><p>父类的静态方法，可以被子类继承</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Foo {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>static classMethod() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>return "hello"</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>class Bar extends Foo {}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>Bar.classMethod(); // 'hello'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>并且，静态方法也是可以从<code>super</code>对象上调用的。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Foo {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>static classMethod() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>return "hello"</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>class Bar extends Foo {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>static classMethod() {</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>return super.classMethod() + ", too"</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>Bar.classMethod() // "hello, too"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Class 的静态属性<a href="#class-的静态属性"><span>#</span></a></h2><p>类的静态属性只要在上面的实例属性写法前面，加上static关键字就可以了。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class MyClass {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>static myStaticProp = 'aa'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>constructor() {</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>console.log(MyClass.myStaticProp) // 'aa'</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/set-yu-map/</id>
      <title type="text">set 与 map</title>
      <published>2021-12-18T18:11:59.000Z</published>
      <updated>2021-12-18T18:11:59.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/set-yu-map/"/>
      <summary type="text">ES6 提供了新的数据结构 Set。它类似于数组， 但是成员的值都是唯一的，没有重复的值 。 Set 本身是一个构造函数，用来生成 Set 数据结构。</summary>
      <content type="html"><![CDATA[<section><h2>set<a href="#set"><span>#</span></a></h2><section><h3>基本用法<a href="#基本用法"><span>#</span></a></h3><p>ES6 提供了新的数据结构 Set。它类似于数组，<strong>但是成员的值都是唯一的，没有重复的值</strong>。</p><p>Set 本身是一个构造函数，用来生成 Set 数据结构。</p><section><h4>set 结构的声明<a href="#set-结构的声明"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>s</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>()</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>s2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>])</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>set 的初始化<a href="#set-的初始化"><span>#</span></a></h4><p>Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数，用来初始化</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>arr</span><span> </span></span><span>=</span><span> [</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>]</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>s</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>arr</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>  </span><span>let</span><span><span> </span><span>s2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>()</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>arr</span><span>.</span><span>forEach</span><span><span>((</span><span>x</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>s</span><span>.</span></span><span>add</span><span><span>(</span><span>x</span><span>))</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>let</span><span><span> </span><span>s3</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>document</span><span>.</span></span><span>querySelectorAll</span><span>(</span><span>"div"</span><span>))</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>set 转换为数组<a href="#set-转换为数组"><span>#</span></a></h4><p>使用<code>...</code>扩展运算符或<code>Array.from</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>s</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>])</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>arr</span><span> </span></span><span>=</span><span> [</span><span>...</span><span><span>s</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>let</span><span><span> </span><span>arr2</span><span> </span></span><span>=</span><span><span> </span><span>Array</span><span>.</span></span><span>from</span><span><span>(</span><span>set</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因此有两种数组去重的方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>s</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>1</span><span>, </span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>, </span><span>2</span><span>])</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>arr</span><span> </span></span><span>=</span><span> [</span><span>...</span><span><span>s</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>let</span><span><span> </span><span>arr2</span><span> </span></span><span>=</span><span><span> </span><span>Array</span><span>.</span></span><span>from</span><span><span>(</span><span>s</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>注<a href="#注"><span>#</span></a></h4><p>向 Set 加入值的时候，不会发生类型转换，所以5和”5”是两个不同的值。Set 内部判断两个值是否不同，使用的算法叫做“Same-value-zero equality”，它类似于精确相等运算符(===)，主要的区别是NaN等于自身，而精确相等运算符认为NaN不等于自身。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>set</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>()</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>NaN</span></div></div><div><div><div>3</div></div><div><span>  </span><span>let</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>NaN</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>set</span><span>.</span><span>add</span><span><span>(</span><span>a</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>set</span><span>.</span><span>add</span><span><span>(</span><span>b</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>// Set {NaN}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码向 Set 实例添加了两个NaN，但是只能加入一个。这表明，在 Set 内部，两个NaN是相等.另外，两个对象总是不相等的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>set</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>()</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>set</span><span>.</span><span>add</span><span>({})</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>set</span><span>.</span><span>size</span><span> </span><span>// 1</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>set</span><span>.</span><span>add</span><span>({})</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>set</span><span>.</span><span>size</span><span> </span><span>// 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>Set 实例的属性和方法<a href="#set-实例的属性和方法"><span>#</span></a></h3><p>属性：</p><ul>
<li><code>Set.prototype.constructor</code>: 构造函数，默认就是Set函数。</li>
<li><code>Set.prototype.size</code>: 返回Set实例的成员总数。</li>
</ul><p>方法：
Set 实例的方法分为两大类: 操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法</p><ul>
<li><code>add(value)</code>: 添加某个值，返回 Set 结构本身。</li>
<li><code>delete(value)</code>: 删除某个值，返回一个布尔值，表示删除是否成功。</li>
<li><code>has(value)</code>: 返回一个布尔值，表示该值是否为Set的成员。</li>
<li><code>clear()</code>: 清除所有成员，没有返回值。</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>add</span><span>(</span><span>1</span><span>).</span><span>add</span><span>(</span><span>2</span><span>).</span><span>add</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>size</span><span> </span><span>// 2</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>has</span><span>(</span><span>1</span><span>) </span><span>// true</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>has</span><span>(</span><span>2</span><span>) </span><span>// true</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>has</span><span>(</span><span>3</span><span>) </span><span>// false</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>delete</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>s</span><span>.</span><span>has</span><span>(</span><span>2</span><span>) </span><span>// false</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>遍历操作<a href="#遍历操作"><span>#</span></a></h3><p>Set 结构的实例有四个遍历方法，可以用于遍历成员。</p><ul>
<li><code>keys()</code>: 返回键名的遍历器</li>
<li><code>values()</code>: 返回键值的遍历器</li>
<li><code>entries()</code>: 返回键值对的遍历器</li>
<li><code>forEach()</code>: 使用回调函数遍历每个成员</li>
</ul><p>需要特别指出的是，<strong>Set的遍历顺序就是插入顺序</strong>。这个特性有时非常有用，比如使用 Set 保存一个回调函数列表，调用时就能保证按照添加顺序调用。</p><section><h4><code>keys()</code>，<code>values()</code>，<code>entries()</code><a href="#keysvaluesentries"><span>#</span></a></h4><p>以上三个方法的返回值都是遍历器对象。由于 Set 结构没有键名，只有键值(或者说键名和键值是同一个值)，所以<code>keys()</code>方法和<code>values()</code>方法的行为完全一致</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>let</span><span><span> </span><span>set</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>"red"</span><span>, </span><span>"green"</span><span>, </span><span>"blue"</span><span>]);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>  </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>item</span><span> </span></span><span>of</span><span><span> </span><span>set</span><span>.</span></span><span>keys</span><span>()) {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>item</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>// red</span></div></div><div><div><div>7</div></div><div><span>  </span><span>// green</span></div></div><div><div><div>8</div></div><div><span>  </span><span>// blue</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>  </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>item</span><span> </span></span><span>of</span><span><span> </span><span>set</span><span>.</span></span><span>values</span><span>()) {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>item</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>  </span><span>// red</span></div></div><div><div><div>14</div></div><div><span>  </span><span>// green</span></div></div><div><div><div>15</div></div><div><span>  </span><span>// blue</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>  </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>item</span><span> </span></span><span>of</span><span><span> </span><span>set</span><span>.</span></span><span>entries</span><span>()) {</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>item</span><span>);</span></span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span>  </span><span>// ["red", "red"]</span></div></div><div><div><div>21</div></div><div><span>  </span><span>// ["green", "green"]</span></div></div><div><div><div>22</div></div><div><span>  </span><span>// ["blue", "blue"]</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码中，<code>entries()</code>方法返回的遍历器，同时包括键名和键值，所以每次输出一个数组，它的两个成员完全相等。</p><p>Set 结构的实例默认可遍历，它的默认遍历器生成函数就是它的<code>values</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>Set</span><span>.</span><span>prototype</span><span><span>[</span><span>Symbol</span><span>.</span></span><span>iterator</span><span>] </span><span>===</span><span> </span><span>Set</span><span>.</span><span>prototype</span><span>.</span><span>values</span><span>  </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>forEach()</code><a href="#foreach"><span>#</span></a></h4><p>Set 结构的实例与数组一样，也拥有<code>forEach</code>方法，用于对每个成员执行某种操作，没有返回值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>set</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>"red"</span><span>, </span><span>"bule"</span><span>, </span><span>"black"</span><span>])</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>set</span><span>.</span><span>forEach</span><span><span>((</span><span>value</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>key</span><span> </span></span><span>+</span><span> </span><span>" : "</span><span> </span><span>+</span><span><span> </span><span>value</span><span>))</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>// red : red</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// bule : bule</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// black : black</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码说明，<code>forEach</code>方法的参数就是一个处理函数。该函数的参数与数组的<code>forEach</code>一致，依次为键值、键名、集合本身(上例省略了该参数)。这里需要注意，Set 结构的键名就是键值(两者是同一个值)，因此第一个参数与第二个参数的值永远都是一样的。
另外，<code>forEach</code>方法还可以有第二个参数，表示绑定处理函数内部的<code>this</code>对象</p></section><section><h4>遍历的应用<a href="#遍历的应用"><span>#</span></a></h4><p>实现并集(Union)、交集(Intersect)和差集(Difference)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>s</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>])</span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>s2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>2</span><span>, </span><span>3</span><span>, </span><span>4</span><span>])</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 并集</span></div></div><div><div><div>5</div></div><div><span>    </span><span>let</span><span><span> </span><span>union</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>...</span><span><span>s</span><span>, </span></span><span>...</span><span><span>s2</span><span>])</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>//交集</span></div></div><div><div><div>7</div></div><div><span>    </span><span>let</span><span><span> </span><span>intersect</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>...</span><span><span>s</span><span>].</span></span><span>filter</span><span><span>(</span><span>num</span><span> </span></span><span>=&gt;</span><span><span> </span><span>s2</span><span>.</span></span><span>has</span><span><span>(</span><span>num</span><span>)))</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>//差集</span></div></div><div><div><div>9</div></div><div><span>    </span><span>let</span><span><span> </span><span>difference</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>...</span><span><span>s</span><span>].</span></span><span>filter</span><span><span>(</span><span>num</span><span> </span></span><span>=&gt;</span><span> </span><span>!</span><span><span>s2</span><span>.</span></span><span>has</span><span><span>(</span><span>num</span><span>)))</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果想在遍历操作中，同步改变原来的 Set 结构，目前没有直接的方法，但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构，然后赋值给原来的 Set 结构；另一种是利用<code>Array.from()</code>方法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>// 方法一</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>set</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>]);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>set</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>...</span><span><span>set</span><span>].</span></span><span>map</span><span><span>(</span><span>val</span><span> </span></span><span>=&gt;</span><span><span> </span><span>val</span><span> </span></span><span>*</span><span> </span><span>2</span><span>));</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// set的值是2, 4, 6</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>// 方法二</span></div></div><div><div><div>7</div></div><div><span>  </span><span>let</span><span><span> </span><span>set</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>]);</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>set</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>Array</span><span>.</span></span><span>from</span><span><span>(</span><span>set</span><span>, </span><span>val</span><span> </span></span><span>=&gt;</span><span><span> </span><span>val</span><span> </span></span><span>*</span><span> </span><span>2</span><span>));</span></div></div><div><div><div>9</div></div><div><span>  </span><span>// set的值是2, 4, 6</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>map<a href="#map"><span>#</span></a></h2><p>JavaScript 的对象(Object)，本质上是键值对的集合(Hash 结构)，但是传统上只能用字符串当作键。这给它的使用带来了很大的限制</p><p>ES6 提供了 Map 数据结构。它类似于对象，也是键值对的集合，但是“键”的范围不限于字符串，各种类型的值(包括对象)都可以当作键。也就是说，Object 结构提供了“字符串—值”的对应，Map 结构提供了“值—值”的对应，是一种更完善的 Hash 结构实现。如果您需要“键值对”的数据结构，Map 比 Object 更合适。</p><section><h3>基本使用<a href="#基本使用"><span>#</span></a></h3><section><h4>map 创建<a href="#map-创建"><span>#</span></a></h4><p>作为构造函数，Map 也可以接受一个数组作为参数。该<strong>数组的成员是一个个表示键值对的数组</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>m</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>[</span><span>"name"</span><span>, </span><span>"tom"</span><span>],</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>[</span><span>"age"</span><span>, </span><span>18</span><span>]</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>])</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>m</span><span>.</span><span>size</span><span> </span><span>//2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码在新建 Map 实例时，就指定了两个键<code>name</code>和<code>age</code>，相当于下面代码</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let item = [</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>['name', 'tom'],</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>['age', 18],</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>let m = new Map()</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>item.forEach(([key, value]) =&gt; m.set(key, value))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>不仅仅是数组，任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。这就是说，Set和Map都可以用来生成新的 Map。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>s</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>[</span><span>"name"</span><span>, </span><span>"tom"</span><span>],</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>[</span><span>"age"</span><span>, </span><span>18</span><span>]</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>])</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>let</span><span><span> </span><span>m</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span><span>(</span><span>s</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>let</span><span><span> </span><span>m2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span><span>(</span><span>m</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>m</span><span> </span></span><span>==</span><span><span> </span><span>m2</span><span>) </span></span><span>//false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>同样的值的两个实例，在 Map 结构中被视为两个键</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>const</span><span> </span><span>map</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>()</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>    </span><span>const</span><span> </span><span>k1</span><span> </span><span>=</span><span> [</span><span>'a'</span><span>]</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>k2</span><span> </span><span>=</span><span> [</span><span>'a'</span><span>]</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>map</span><span>.</span><span>set</span><span><span>(</span><span>k1</span><span>, </span></span><span>111</span><span>).</span><span>set</span><span><span>(</span><span>k2</span><span>, </span></span><span>222</span><span>)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>map</span><span>.</span><span>get</span><span><span>(</span><span>k1</span><span>) </span></span><span>// 111</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>map</span><span>.</span><span>get</span><span><span>(</span><span>k2</span><span>) </span></span><span>// 222</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>变量k1和k2的值是一样的，但是它们在 Map 结构中被视为两个键
由上可知，Map 的键实际上是跟内存地址绑定的，只要内存地址不一样，就视为两个键。这就解决了同名属性碰撞(clash)的问题，我们扩展别人的库的时候，如果使用对象作为键名，就不用担心自己的属性与原作者的属性同名</p></section></section><section><h3>map 的属性和操作方法<a href="#map-的属性和操作方法"><span>#</span></a></h3><section><h4><code>size</code> 属性<a href="#size-属性"><span>#</span></a></h4><p><code>size</code>属性返回map的成员总数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>let map = new Map()</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>map.set("foo", 1)</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>map.set("bar", 2)</span></div></div><div><div><div>4</div></div><div><span>// map.size == 3</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>set(key, value)</code><a href="#setkey-value"><span>#</span></a></h4><p><code>set</code>方法设置键名<code>key</code>对应的键值为value<code>，</code>然后返回整个 Map 结构。如果<code>key</code>已经有值，则键值会被更新，否则就新生成该键</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span> </span><span>let</span><span><span> </span><span>m</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>()</span></div></div><div><div><div>2</div></div><div><span> </span><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>"abc"</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>m</span><span>.</span><span>set</span><span><span>(</span><span>a</span><span>, </span></span><span>3</span><span>) </span><span>//键是变量</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>m</span><span>.</span><span>set</span><span>(</span><span>"edition"</span><span>, </span><span>6</span><span>) </span><span>// 键是字符串</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>m</span><span>.</span><span>set</span><span>(</span><span>262</span><span>, </span><span>"standard"</span><span>) </span><span>// 键是数值</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>m</span><span>.</span><span>set</span><span>(</span><span>undefined</span><span>, </span><span>"nah"</span><span>) </span><span>// 键是 undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>set()</code>方法返回的是当前的Map对象，因此可以采用链式写法。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>"abc"</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>m</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>().</span><span>set</span><span><span>(</span><span>a</span><span>, </span></span><span>3</span><span>).</span><span>set</span><span>(</span><span>"edition"</span><span>, </span><span>6</span><span>).</span><span>set</span><span>(</span><span>262</span><span>, </span><span>"standard"</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>get(key)</code><a href="#getkey"><span>#</span></a></h4><p><code>get()</code> 方法读取key所对应的键值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span> </span><span>let</span><span><span> </span><span>map</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>[</span><span>"foo"</span><span>, </span><span>1</span><span>]</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>])</span></div></div><div><div><div>4</div></div><div><span><span>map</span><span>.</span></span><span>get</span><span>(</span><span>"foo"</span><span>) </span><span>// 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>has(key)</code><a href="#haskey"><span>#</span></a></h4><p><code>has()</code>方法返回一个布尔值，表示某个键是否在当前 Map 对象之中</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span> </span><span>let</span><span><span> </span><span>map</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>[</span><span>"foo"</span><span>, </span><span>1</span><span>]</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>])</span></div></div><div><div><div>4</div></div><div><span><span>map</span><span>.</span></span><span>has</span><span>(</span><span>"foo"</span><span>) </span><span>// true</span></div></div><div><div><div>5</div></div><div><span><span>map</span><span>.</span></span><span>has</span><span>(</span><span>"bar"</span><span>) </span><span>//false</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>delete(key)</code><a href="#deletekey"><span>#</span></a></h4><p><code>delete()</code>方法删除某个键，返回<code>true</code>。如果删除失败，返回false</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span> </span><span>let</span><span><span> </span><span>map</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>[</span><span>"foo"</span><span>, </span><span>1</span><span>]</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>])</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>map</span><span>.</span><span>delete</span><span>(</span><span>"foo"</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>map</span><span>.</span></span><span>has</span><span>(</span><span>"foo"</span><span>)) </span><span>// false</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4><code>clear()</code><a href="#clear"><span>#</span></a></h4><p><code>clear()</code>方法清除所有成员，没有返回值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>map</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>[</span><span>"foo"</span><span>, </span><span>1</span><span>],</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>[</span><span>"bar"</span><span>, </span><span>2</span><span>]</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>])</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>map</span><span>.</span><span>clear</span><span>()</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>map</span><span>.</span><span>size</span><span> </span><span>//0</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>map 的遍历操作<a href="#map-的遍历操作"><span>#</span></a></h3><p>Map 结构原生提供三个遍历器生成函数和一个遍历方法。</p><ul>
<li><code>keys()</code>: 返回键名的遍历器。</li>
<li><code>values()</code>: 返回键值的遍历器。</li>
<li><code>entries()</code>: 返回所有成员的遍历器。</li>
<li><code>forEach()</code>: 遍历 Map 的所有成员</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>let</span><span><span> </span><span>map</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>[</span><span>"foo"</span><span>, </span><span>1</span><span>],</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>[</span><span>"bar"</span><span>, </span><span>2</span><span>]</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>])</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> </span><span>key</span><span> </span><span>of</span><span><span> </span><span>map</span><span>.</span></span><span>keys</span><span>()) {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>key</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>    </span><span>// foo , bar</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> </span><span>value</span><span> </span><span>of</span><span><span> </span><span>map</span><span>.</span></span><span>values</span><span>()) {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>    </span><span>// 1 , 2</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> </span><span>item</span><span> </span><span>of</span><span><span> </span><span>map</span><span>.</span></span><span>entries</span><span>()) {</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>item</span><span>)</span></span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>    </span><span>// ["foo", 1] , ["bar", 2]</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> [</span><span>key</span><span>, </span><span>value</span><span>] </span><span>of</span><span><span> </span><span>map</span><span>.</span></span><span>entries</span><span>()) {</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>key</span><span>, </span><span>value</span><span>)</span></span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>    </span><span>// ["foo", 1] , ["bar", 2]</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// 等同于使用map.entries()</span></div></div><div><div><div>27</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> [</span><span>key</span><span>, </span><span>value</span><span>] </span><span>of</span><span><span> </span><span>map</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>key</span><span>, </span><span>value</span><span>);</span></span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span>    </span><span>// ["foo", 1] , ["bar", 2]</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码最后的那个例子，表示 Map 结构的默认遍历器接口(<code>Symbol.iterator</code>属性)，就是<code>entries()</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>map[Symbol.iterator] === map.entries</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>结合数组的<code>map</code>方法、<code>filter</code>方法，可以实现 Map 的遍历和过滤(Map 本身没有<code>map</code>和<code>filter</code>方法)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>const</span><span> </span><span>map0</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>().</span><span>set</span><span>(</span><span>1</span><span>, </span><span>"a"</span><span>).</span><span>set</span><span>(</span><span>2</span><span>, </span><span>"b"</span><span>).</span><span>set</span><span>(</span><span>3</span><span>, </span><span>"c"</span><span>)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>map1</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span><span>...</span><span><span>map0</span><span>].</span></span><span>filter</span><span><span>(([</span><span>k</span><span>, </span><span>v</span><span>]) </span></span><span>=&gt;</span><span><span> </span><span>k</span><span> </span></span><span>&lt;</span><span> </span><span>3</span><span>))</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// 产生 Map 结构 {1 =&gt; 'a', 2 =&gt; 'b'}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>map2</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span><span>...</span><span><span>map0</span><span>].</span></span><span>map</span><span><span>(([</span><span>k</span><span>, </span><span>v</span><span>]) </span></span><span>=&gt;</span><span><span> [</span><span>k</span><span> </span></span><span>*</span><span> </span><span>2</span><span>, </span><span>"_"</span><span> </span><span>+</span><span><span> </span><span>v</span><span>]))</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>// 产生 Map 结构 {2 =&gt; '_a', 4 =&gt; '_b', 6 =&gt; '_c'}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/ajax-kua-yu/</id>
      <title type="text">Ajax跨域</title>
      <published>2021-12-06T00:08:27.000Z</published>
      <updated>2021-12-06T00:08:27.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/ajax-kua-yu/"/>
      <summary type="text">同源策略（Sameoriginpolicy）是一种约定，它是浏览器最核心也最基本的安全功能，如果缺少了同源策略，则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的，浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。 所…</summary>
      <content type="html"><![CDATA[<section><h2>同源策略<a href="#同源策略"><span>#</span></a></h2><p>同源策略（Sameoriginpolicy）是一种约定，它是浏览器最核心也最基本的安全功能，如果缺少了同源策略，则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的，浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
<strong>所谓同源（即指在同一个域）就是两个页面具有相同的协议（protocol），主机（host）和端口号（port）</strong></p></section>
<section><h2>什么是跨域<a href="#什么是跨域"><span>#</span></a></h2><p>当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域</p>

<table><thead><tr><th>当前页面url</th><th>被请求页面url</th><th>是否跨域</th><th>原因</th></tr></thead><tbody><tr><td><a href="http://www.test.com/" target="_blank">http://www.test.com/</a></td><td><a href="http://www.test.com/index.html" target="_blank">http://www.test.com/index.html</a></td><td>否</td><td>同源（协议、域名、端口号相同）</td></tr><tr><td><a href="http://www.test.com/" target="_blank">http://www.test.com/</a></td><td><a href="https://www.test.com/index.html" target="_blank">https://www.test.com/index.html</a></td><td>跨域</td><td>协议不同（http/https）</td></tr><tr><td><a href="http://www.test.com/" target="_blank">http://www.test.com/</a></td><td><a href="http://www.baidu.com/" target="_blank">http://www.baidu.com/</a></td><td>跨域</td><td>主域名不同（test/baidu）</td></tr><tr><td><a href="http://www.test.com/" target="_blank">http://www.test.com/</a></td><td><a href="http://blog.test.com/" target="_blank">http://blog.test.com/</a></td><td>跨域</td><td>子域名不同（www/blog）</td></tr><tr><td><a href="http://www.test.com:8080/" target="_blank">http://www.test.com:8080/</a></td><td><a href="http://www.test.com:7001/" target="_blank">http://www.test.com:7001/</a></td><td>跨域</td><td>端口号不同（8080/7001）</td></tr></tbody></table></section>
<section><h2>非同源限制<a href="#非同源限制"><span>#</span></a></h2><ul>
<li>
<p>无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB</p>
</li>
<li>
<p>无法接触非同源网页的 DOM</p>
</li>
<li>
<p>无法向非同源地址发送 AJAX 请求</p>
</li>
</ul></section>
<section><h2>跨域解决方法<a href="#跨域解决方法"><span>#</span></a></h2><section><h3>JSONP<a href="#jsonp"><span>#</span></a></h3><p>JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用，兼容性好（兼容低版本IE），缺点是只支持get请求，不支持post请求。</p><p>核心思想：网页通过添加一个<code>&lt;script&gt;</code>元素，向服务器请求 JSON 数据，服务器收到请求后，将数据放在一个指定名字的回调函数的参数位置传回来。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 处理服务器返回回调函数的数据</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;script type="text/javascript"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>function dosomething(res){</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>// 处理获得的数据</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>console.log(res.data)</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;/script&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>// 向服务器127.0.0.1:8000/test发出请求，</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>该请求的查询字符串有一个callback参数，用来指定回调函数的名字</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;script src="http://127.0.0.1:8000/test?callback=dosomething"&gt;&lt;/script&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>// node 部分</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>app.all('/test',(request, response) =&gt; {</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>// response.send('console.log("hello jsonp")');</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>const data = {</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>data: "data"</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>};</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>//将数据转化为字符串</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>let str = JSON.stringify(data)</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>//返回结果</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>response.end(`dosomething(${str})`)</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>// 返回的结果 dosomething({"data":"data"})</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>CORS<a href="#cors"><span>#</span></a></h3><p>CORS 是跨域资源分享（Cross-Origin Resource Sharing）的缩写。它是 W3C 标准，属于跨源 AJAX 请求的根本解决方法
服务器端对于CORS的支持，主要是通过设置<code>Access-Control-Allow-Origin</code>来进行的。如果浏览器检测到相应的设置，就可以允许Ajax进行跨域的访问</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// node</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>app.all('/cors-server', (request, response)=&gt;{</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>//设置响应头</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>response.setHeader("Access-Control-Allow-Origin", "*")</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5500")</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>response.send('hello CORS');</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/axios/</id>
      <title type="text">Axios</title>
      <published>2021-11-25T05:35:27.000Z</published>
      <updated>2021-11-25T05:35:27.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/axios/"/>
      <summary type="text">1. REST API: restful （Representational State Transfer (资源)表现层状态转化） 1. 发送请求进行CRUD 哪个操作由请求方式来决定 2. 同一个请求路径可以进行多个操作 3. 请求方式会用到GET/POST/PUT/DELETE 2. 非RES…</summary>
      <content type="html"><![CDATA[<section><h2>创建 API<a href="#创建-api"><span>#</span></a></h2><section><h3>API 的分类<a href="#api-的分类"><span>#</span></a></h3><ol>
<li>REST API: restful （Representational State Transfer (资源)表现层状态转化）
<ol>
<li>发送请求进行CRUD 哪个操作由请求方式来决定</li>
<li>同一个请求路径可以进行多个操作</li>
<li>请求方式会用到GET/POST/PUT/DELETE</li>
</ol>
</li>
<li>非REST API: restless
<ol>
<li>请求方式不决定请求的CRUD 操作</li>
<li>一个请求路径只对应一个操作</li>
<li>一般只有GET/POST</li>
</ol>
</li>
</ol></section><section><h3>使用<a href="https://github.com/typicode/json-server" target="_blank">json-server</a> 搭建REST API<a href="#使用json-server-搭建rest-api"><span>#</span></a></h3><section><h4>json-server<a href="#json-server"><span>#</span></a></h4><p>json-server 是用来快速搭建REST API 的工具包</p></section><section><h4>使用json-server<a href="#使用json-server"><span>#</span></a></h4><ol>
<li><a href="https://github.com/typicode/json-server" target="_blank">在线文档: https://github.com/typicode/json-server</a></li>
<li>下载: <code>npm install -g json-server</code></li>
<li>目标根目录下创建数据库 json 文件: <code>db.json</code></li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>"posts": [</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{ "id": 1, "title": "json-server", "author": "typicode" },</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>{ "id": 2, "title": "json-server2", "author": "typicode" }</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>"comments": [</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>{ "id": 1, "body": "some comment", "postId": 1 }</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>"profile": { "name": "typicode" }</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>启动服务器执行命令: <code>json-server --watch db.json</code></li>
<li>使用浏览器访问测试 <code>http://localhost:3000/posts</code></li>
</ol></section></section></section>
<section><h2>XHR 的 ajax封装 (简单版axios)<a href="#xhr-的-ajax封装-简单版axios"><span>#</span></a></h2><section><h3>特点<a href="#特点"><span>#</span></a></h3><ol>
<li>函数的返回值为<code>promise</code>, 成功的结果为<code>response</code>, 失败的结果为<code>error</code></li>
<li>能处理多种类型的请求: GET/POST/PUT/DELETE</li>
<li>函数的参数为一个配置对象</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>url: '', // 请求地址</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>method: '', // 请求方式GET/POST/PUT/DELETE</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>params: {}, // GET/DELETE 请求的 query 参数</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>data: {}, // POST/PUT 请求的请求体参数</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>响应 <code>json</code>数据 自动解析为 <code>js</code>的对象/数组</li>
</ol></section><section><h3>编码实现<a href="#编码实现"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function axios({ url, method = 'GET', params = {}, data = {} }) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 返回一个promise对象</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>return new Promise((resolve, reject) =&gt; {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>// 处理method 转大写</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>method = method.toUpperCase()</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>// 处理 query参数（拼接到url上）id=1&amp;xxx=abc</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>let queryString = ''</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>Object.keys(params).forEach((key) =&gt; {</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>queryString += `${key}=${params[key]}&amp;`</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>if (queryString) {</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>// 去除最后的'&amp;'</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>queryString = queryString.substring(0, queryString.length - 1)</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>// 接到url</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>url += '?' + queryString</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>// 1. 执行异步Ajax请求</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>// 创建xhr对象</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>const request = new XMLHttpRequest()</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>// 打开连接（初始化请求，没有请求）</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>request.open(method, url, true)</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>// 发送请求</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>if (method === 'GET' || method === 'DELETE') {</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>request.send() // undefined</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>} else if (method === 'POST' || method === 'PUT') {</span></div></div><div><div><div>28</div></div><div><span><span>            </span></span><span>// 设置请求头，告诉服务器请求体的格式是json</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>request.setRequestHeader(</span></div></div><div><div><div>30</div></div><div><span><span>                </span></span><span>'Content-Type',</span></div></div><div><div><div>31</div></div><div><span><span>                </span></span><span>'appliaction/json;charset=utf-8'</span></div></div><div><div><div>32</div></div><div><span><span>            </span></span><span>)</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>// 发送json格式请求参数</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>request.send(JSON.stringify(data)) // 异步执行</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>// 绑定状态改变的监听</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>request.onreadystatechange = function () {</span></div></div><div><div><div>39</div></div><div><span><span>            </span></span><span>// 同步执行</span></div></div><div><div><div>40</div></div><div><span><span>            </span></span><span>// 如果请求没有完成，直接结束</span></div></div><div><div><div>41</div></div><div><span><span>            </span></span><span>if (request.readyState !== 4) {</span></div></div><div><div><div>42</div></div><div><span><span>                </span></span><span>return</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>44</div></div><div><span><span>            </span></span><span>// 如果响应状态码在[200, 300)之间代表成功，否则失败</span></div></div><div><div><div>45</div></div><div><span><span>            </span></span><span>const { status, statusText } = request</span></div></div><div><div><div>46</div></div><div><span><span>            </span></span><span>if (status &gt;= 200 &amp;&amp; status &lt;= 299) {</span></div></div><div><div><div>47</div></div><div><span><span>                </span></span><span>// 2.1 如果请求成功，调用resolve()</span></div></div><div><div><div>48</div></div><div><span><span>                </span></span><span>// 准备结果数据对象response</span></div></div><div><div><div>49</div></div><div><span><span>                </span></span><span>const response = {</span></div></div><div><div><div>50</div></div><div><span><span>                    </span></span><span>data: JSON.parse(request.response),</span></div></div><div><div><div>51</div></div><div><span><span>                    </span></span><span>status,</span></div></div><div><div><div>52</div></div><div><span><span>                    </span></span><span>statusText,</span></div></div><div><div><div>53</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span><span>                </span></span><span>resolve(response)</span></div></div><div><div><div>55</div></div><div><span><span>            </span></span><span>} else {</span></div></div><div><div><div>56</div></div><div><span><span>                </span></span><span>// 2.2 如果请求失败，调用reject()</span></div></div><div><div><div>57</div></div><div><span><span>                </span></span><span>reject(new Error('request error status is ' + status))</span></div></div><div><div><div>58</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>59</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>使用测试<a href="#使用测试"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. GET请求：从服务器端获取数据</span></div></div><div><div><div>2</div></div><div><span>function testGet() {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>url: 'http://localhost:3000/posts',</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>method: 'GET',</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>params: {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>id: 1,</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>xxx: 'abc',</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>console.log(response)</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>alert(error.message)</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 2. POST请求：向服务器端添加数据</span></div></div><div><div><div>21</div></div><div><span>function testPost() {</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>url: 'http://localhost:3000/posts',</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>method: 'POST',</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>data: {</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>title: 'json-server_post',</span></div></div><div><div><div>27</div></div><div><span><span>            </span></span><span>author: 'typicode_post',</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>console.log(response)</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>alert(error.message)</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>37</div></div><div><span>}</span></div></div><div><div><div>38</div></div><div><span>// 3. PUT请求：服务器更新数据</span></div></div><div><div><div>39</div></div><div><span>function testPut() {</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>41</div></div><div><span><span>        </span></span><span>url: 'http://localhost:3000/posts/1',</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>method: 'PUT',</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>data: {</span></div></div><div><div><div>44</div></div><div><span><span>            </span></span><span>title: 'json-server_put',</span></div></div><div><div><div>45</div></div><div><span><span>            </span></span><span>author: 'typicode_put',</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>49</div></div><div><span><span>            </span></span><span>console.log(response)</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>51</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>alert(error.message)</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>// 4. DELETE请求：服务器删除数据</span></div></div><div><div><div>58</div></div><div><span>function testDelete() {</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>60</div></div><div><span><span>        </span></span><span>url: 'http://localhost:3000/posts/2',</span></div></div><div><div><div>61</div></div><div><span><span>        </span></span><span>method: 'delete',</span></div></div><div><div><div>62</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>64</div></div><div><span><span>            </span></span><span>console.log(response)</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>67</div></div><div><span><span>            </span></span><span>alert(error.message)</span></div></div><div><div><div>68</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>69</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>70</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>axios 的理解和使用<a href="#axios-的理解和使用"><span>#</span></a></h2><section><h3>axios 是什么<a href="#axios-是什么"><span>#</span></a></h3><ol>
<li>前端最流行的 ajax请求库</li>
<li>react/vue 官方都推荐使用 axios 发ajax 请求</li>
<li><a href="https://github.com/axios/axios" target="_blank">文档: https://github.com/axios/axios</a></li>
</ol></section><section><h3>axios 特点<a href="#axios-特点"><span>#</span></a></h3><ol>
<li>基于 xhr + promise 的异步 ajax请求库</li>
<li>浏览器端/node 端都可以使用</li>
<li>支持请求／响应拦截器</li>
<li>支持请求取消</li>
<li>请求/响应数据转换</li>
<li>批量发送多个请求</li>
</ol></section><section><h3>axios 常用语法<a href="#axios-常用语法"><span>#</span></a></h3><ul>
<li><code>axios(config)</code>: 通用/最本质的发任意类型请求的方式</li>
<li><code>axios(url[, config])</code>: 可以只指定url 发get 请求</li>
<li><code>axios.request(config)</code>: 等同于axios(config)</li>
<li><code>axios.get(url[, config])</code>: 发get 请求</li>
<li><code>axios.delete(url[, config])</code>: 发delete 请求</li>
<li><code>axios.post(url[, data, config])</code>: 发post 请求</li>
<li><code>axios.put(url[, data, config])</code>: 发put 请求</li>
<li><code>axios.defaults.xxx</code>: 请求的默认全局配置（method\baseURL\params\timeout…）</li>
<li><code>axios.interceptors.request.use()</code>: 添加请求拦截器</li>
<li><code>axios.interceptors.response.use()</code>: 添加响应拦截器</li>
<li><code>axios.create([config])</code>: 创建一个新的axios(它没有下面的功能)</li>
<li><code>axios.Cancel()</code>: 用于创建取消请求的错误对象</li>
<li><code>axios.CancelToken()</code>: 用于创建取消请求的 token 对象</li>
<li><code>axios.isCancel()</code>: 是否是一个取消请求的错误</li>
<li><code>axios.all(promises)</code>: 用于批量执行多个异步请求</li>
<li><code>axios.spread()</code>: 用来指定接收所有成功数据的回调函数的方法</li>
</ul></section><section><h3>使用示例<a href="#使用示例"><span>#</span></a></h3><section><h4>GET 请求<a href="#get-请求"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 为给定 ID 的 user 创建请求</span></div></div><div><div><div>2</div></div><div><span>axios</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>.get('/user?ID=12345')</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>.then(function (response) {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>console.log(response)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>.catch(function (error) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>console.log(error)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 可选地，上面的请求可以这样做</span></div></div><div><div><div>12</div></div><div><span>axios</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>.get('/user', {</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>params: {</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>ID: 12345,</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>.then(function (response) {</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>console.log(response)</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>.catch(function (error) {</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>console.log(error)</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>通过语法糖发送GET请求<a href="#通过语法糖发送get请求"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>axios(config)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 发送 POST 请求</span></div></div><div><div><div>4</div></div><div><span>axios({</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>method: 'post',</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>url: '/user/12345',</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>data: {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>firstName: 'Fred',</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>lastName: 'Flintstone',</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>11</div></div><div><span>})</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 发送 GET 请求（默认的方法）</span></div></div><div><div><div>14</div></div><div><span>axios('/user/12345')</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 发送 GET 请求（axios.get方法）</span></div></div><div><div><div>17</div></div><div><span>axios.get('/user/12345')</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section></section>
<section><h2>重点注意<a href="#重点注意"><span>#</span></a></h2><section><h3><code>axios.create(config)</code><a href="#axioscreateconfig"><span>#</span></a></h3><ol>
<li>根据指定配置创建一个新的 axios, 也就是每个新 axios 都有自己的配置</li>
<li>新 axios 只是没有<strong>取消请求</strong>和<strong>批量发请求</strong>的方法, 其它所有语法都是一致的</li>
<li>为什么要设计这个语法?
<ul>
<li>需求: 项目中有部分接口需要的配置与另一部分接口需要的配置不太一样, 如何处理（比如有多个baseURL需要指定）</li>
<li>解决: 创建2 个新axios, 每个都有自己特有的配置, 分别应用到不同要求的接口请求中</li>
</ul>
</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const instance = axios.create({</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// instance是函数类型</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>baseURL: 'http://localhost:3000',</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>// 使用instance发Ajax请求</span></div></div><div><div><div>6</div></div><div><span>instance({</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>url: '/posts',</span></div></div><div><div><div>8</div></div><div><span>})</span></div></div><div><div><div>9</div></div><div><span>instance.get('/posts')</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>拦截器函数的调用顺序<a href="#拦截器函数的调用顺序"><span>#</span></a></h3><ol>
<li>说明: 调用<code>axios()</code>并不是立即发送ajax 请求, 而是需要经历一个较长的流程</li>
<li>流程: 请求拦截器2 =&gt; 请求拦截器1 =&gt; 发ajax 请求 =&gt; 响应拦截器1 =&gt; 响应拦截器2 =&gt; 请求的回调</li>
<li>注意: 此流程是通过 promise 串连起来的, 请求拦截器传递的是<code>config</code>, 响应拦截器传递的是<code>response</code></li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 添加两个请求拦截器(回调函数)</span></div></div><div><div><div>2</div></div><div><span>axios.interceptors.request.use(</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>(config) =&gt; {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>console.log('request interceptor1 onResolved()') // -----------2</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>return config</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>(error) =&gt; {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>console.log('request interceptor1 onRejected()')</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>return Promise.reject(error)</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>)</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>axios.interceptors.request.use(</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>(config) =&gt; {</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>console.log('request interceptor2 onResolved()') // -----------1</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>return config</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>(error) =&gt; {</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>console.log('request interceptor2 onRejected()')</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>return Promise.reject(error)</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>)</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>// 添加两个响应拦截器</span></div></div><div><div><div>25</div></div><div><span>axios.interceptors.response.use(</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>(resopnse) =&gt; {</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>console.log('response interceptor1 onResolved()') // -----------3</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>return resopnse</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>(error) =&gt; {</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>console.log('response interceptor1 onRejected()')</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>return Promise.reject(error)</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span>)</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>axios.interceptors.response.use(</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>(resopnse) =&gt; {</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>console.log('response interceptor2 onResolved()') // -----------4</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>return resopnse</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>(error) =&gt; {</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>console.log('response interceptor2 onRejected()')</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>return Promise.reject(error)</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>45</div></div><div><span>)</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>axios</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>.get('http://localhost:3000/posts')</span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>.then((response) =&gt; {</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>console.log('data', response.data) //data Array(4) -------------5</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>.catch((error) =&gt; {</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>cosole.log('error', error.message)</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>// request interceptor2 onResolved()</span></div></div><div><div><div>57</div></div><div><span>// request interceptor1 onResolved()</span></div></div><div><div><div>58</div></div><div><span>// response interceptor1 onResolved()</span></div></div><div><div><div>59</div></div><div><span>// response interceptor2 onResolved()</span></div></div><div><div><div>60</div></div><div><span>// data Array(4)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>取消请求<a href="#取消请求"><span>#</span></a></h3><section><h4>基本流程<a href="#基本流程"><span>#</span></a></h4><ol>
<li>配置 <code>cancelToken</code> 对象</li>
<li>缓存用于取消请求的 <code>cancel</code> 函数</li>
<li>在后面特定时机调用 <code>cancel</code> 函数取消请求</li>
<li>在错误回调中判断如果 <code>error</code> 是 <code>cancel</code>, 做相应处理</li>
</ol></section><section><h4>实现功能<a href="#实现功能"><span>#</span></a></h4><p>在请求一个接口前, 取消前面一个未完成的请求</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let cancel // 用于保存取消请求的函数</span></div></div><div><div><div>2</div></div><div><span>function getProducts1() {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// 在准备发请求前，取消未完成的请求</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>if (typeof cancel === 'function') {</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>cancel('取消请求')</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>url: 'http://localhost:4000/products1',</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>cancelToken: new axios.CancelToken(function executor(c) {</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>// c是用于取消当前请求的函数</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>// 保存取消函数，用于之后可能需要取消当前请求</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>cancel = c</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>}),</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>cancel = null</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>console.log('请求1成功了', response.data)</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>if (axios.isCancel(error)) {</span></div></div><div><div><div>21</div></div><div><span><span>                </span></span><span>console.log('请求1取消的错误', error.message)</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>} else {</span></div></div><div><div><div>23</div></div><div><span><span>                </span></span><span>// 请求出错了</span></div></div><div><div><div>24</div></div><div><span><span>                </span></span><span>cancel = null</span></div></div><div><div><div>25</div></div><div><span><span>                </span></span><span>console.log('请求1失败了', error.message, error) // 请求1失败了 强制取消请求 Cancel {message: "强制取消请求"}</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>function getProducts2() {</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>// 在准备发请求前，取消未完成的请求</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>if (typeof cancel === 'function') {</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>cancel('取消请求')</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>url: 'http://localhost:4000/products2',</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>cancelToken: new axios.CancelToken(function executor(c) {</span></div></div><div><div><div>39</div></div><div><span><span>            </span></span><span>cancel = c</span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>}),</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>cancel = null</span></div></div><div><div><div>44</div></div><div><span><span>            </span></span><span>console.log('请求2成功了', response.data)</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>47</div></div><div><span><span>            </span></span><span>if (axios.isCancel(error)) {</span></div></div><div><div><div>48</div></div><div><span><span>                </span></span><span>console.log('请求2取消的错误', error.message)</span></div></div><div><div><div>49</div></div><div><span><span>            </span></span><span>} else {</span></div></div><div><div><div>50</div></div><div><span><span>                </span></span><span>cancel = null</span></div></div><div><div><div>51</div></div><div><span><span>                </span></span><span>console.log('请求2失败了', error.message, error)</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div><div><div><div>56</div></div><div><span>function cancelReq() {</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>// 执行取消请求的函数</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>if (typeof cancel === 'function') {</span></div></div><div><div><div>59</div></div><div><span><span>        </span></span><span>cancel('强制取消请求')</span></div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>} else {</span></div></div><div><div><div>61</div></div><div><span><span>        </span></span><span>console.log('没有可取消的请求')</span></div></div><div><div><div>62</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>63</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>使用拦截器，减少重复代码<a href="#使用拦截器减少重复代码"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 添加请求拦截器</span></div></div><div><div><div>2</div></div><div><span>axios.interceptors.request.use((config) =&gt; {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// 只写一个成功的回调</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>// 在准备发请求前，取消未完成的请求</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>if (typeof cancel === 'function') {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>cancel('取消请求')</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// 添加一个cancelToken的配置</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>config.cancelToken = new axios.CancelToken(function executor(c) {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>// c是用于取消当前请求的函数</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>// 保存取消函数，用于之后可能需要取消当前请求</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>cancel = c</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>return config</span></div></div><div><div><div>15</div></div><div><span>})</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// 添加响应拦截器</span></div></div><div><div><div>18</div></div><div><span>axios.interceptors.response.use(</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>(response) =&gt; {</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>// 成功的回调</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>cancel = null</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>return response</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>(error) =&gt; {</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>// 失败的回调</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>if (axios.isCancel(error)) {</span></div></div><div><div><div>27</div></div><div><span><span>            </span></span><span>// 请求取消的错误</span></div></div><div><div><div>28</div></div><div><span><span>            </span></span><span>console.log('请求取消的错误', error.message)</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>// 中断promise链</span></div></div><div><div><div>30</div></div><div><span><span>            </span></span><span>return new Promise(() =&gt; {})</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>} else {</span></div></div><div><div><div>32</div></div><div><span><span>            </span></span><span>// 请求出错了</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>cancel = null</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>// 将错误向下传递</span></div></div><div><div><div>35</div></div><div><span><span>            </span></span><span>// throw error</span></div></div><div><div><div>36</div></div><div><span><span>            </span></span><span>return Promise.reject(error)</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span>)</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>let cancel // 用于保存取消请求的函数</span></div></div><div><div><div>42</div></div><div><span>function getProducts1() {</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>url: 'http://localhost:4000/products1',</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>47</div></div><div><span><span>            </span></span><span>console.log('请求1成功了', response.data)</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>50</div></div><div><span><span>            </span></span><span>// 只用处理请求失败的情况，取消请求的错误不用处理</span></div></div><div><div><div>51</div></div><div><span><span>            </span></span><span>console.log('请求1失败了', error.message, error)</span></div></div><div><div><div>52</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>53</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>54</div></div><div><span>}</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>function getProducts2() {</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>axios({</span></div></div><div><div><div>58</div></div><div><span><span>        </span></span><span>url: 'http://localhost:4000/products2',</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>}).then(</span></div></div><div><div><div>60</div></div><div><span><span>        </span></span><span>(response) =&gt; {</span></div></div><div><div><div>61</div></div><div><span><span>            </span></span><span>console.log('请求2成功了', response.data)</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>(error) =&gt; {</span></div></div><div><div><div>64</div></div><div><span><span>            </span></span><span>console.log('请求2失败了', error.message, error)</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>66</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>67</div></div><div><span>}</span></div></div><div><div><div>68</div></div><div><span>function cancelReq() {</span></div></div><div><div><div>69</div></div><div><span><span>    </span></span><span>if (typeof cancel === 'function') {</span></div></div><div><div><div>70</div></div><div><span><span>        </span></span><span>cancel('强制取消请求')</span></div></div><div><div><div>71</div></div><div><span><span>    </span></span><span>} else {</span></div></div><div><div><div>72</div></div><div><span><span>        </span></span><span>console.log('没有可取消的请求')</span></div></div><div><div><div>73</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>74</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/fetch/</id>
      <title type="text">Fetch</title>
      <published>2021-11-25T05:35:27.000Z</published>
      <updated>2021-11-25T05:35:27.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/fetch/"/>
      <summary type="text">fetch 是一种 HTTP 数据请求的方式，是 XMLHttpRequest 的一种替代方案。fetch 不是 ajax 的进一步封装，而是原生 js。Fetch 函数就是原生 js，没有使用 XMLHttpRequest 对象</summary>
      <content type="html"><![CDATA[<p>fetch 是一种 HTTP 数据请求的方式，是 XMLHttpRequest 的一种替代方案。fetch 不是 ajax 的进一步封装，而是原生 js。Fetch 函数就是原生 js，没有使用 XMLHttpRequest 对象</p>
<section><h2>Fetch 的参数<a href="#fetch-的参数"><span>#</span></a></h2><ul>
<li><code>url</code> ,该参数定义要获取的资源,可以是一个 url 字符串,也可以是一个 Request 对象</li>
<li><code>options</code> ,该参数可选,为一个对象,内部含有一些对请求的设置属性
<ul>
<li><code>method</code>&lt;请求使用的方法&gt;。如、POST 等</li>
<li><code>headers</code>&lt;请求头信息&gt;。形式为 Headers 对象或 ByteString</li>
<li><code>body</code>&lt;请求的&gt; body 信息,可能是一个 Blob、BufferSource、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息</li>
<li><code>mode</code>&lt;请求的模式&gt;。如 cors、no-cors 或者 same-origin</li>
<li><code>credentiala</code>&lt;请求的&gt; credentials。如 omit、same-origin 或者 include</li>
<li><code>cache</code>&lt;请求的&gt; cache 模式,no-store,reload,no-cache 或者 only-if-cached</li>
</ul>
</li>
</ul><section><h3>response 对象<a href="#response-对象"><span>#</span></a></h3><p>response 为一个 Promise 对象成功时传回的对象,该属性含有对应返回数据的属性和方法</p><section><h4>属性<a href="#属性"><span>#</span></a></h4><ul>
<li><code>status</code> (number),HTTP 请求结果参数,在 100-599 范围</li>
<li><code>statusText</code> (string),服务器返回的状态报告</li>
<li><code>ok</code> (boolean),如果返回 200 表示请求成功则为 true</li>
<li><code>headers</code> (Headers),返回头部信息,该属性后面也有对应的方法</li>
<li><code>has(name)</code> (boolean),判断是否存在该信息头</li>
<li><code>get(name)</code> (string),获取信息头的数据</li>
<li><code>getAll(name)</code> (Array),获取所有头部数据</li>
<li><code>set(name,value)</code>,设置请求头信息</li>
<li><code>append(name,value)</code>,添加 header 的内容</li>
<li><code>delete(name)</code>,删除 header 的信息</li>
<li><code>forEach(function(value,name){...},[thisContext])</code>,循环读取 header 的信息</li>
<li><code>url</code> (string),详细的地址</li>
</ul></section><section><h4>方法<a href="#方法"><span>#</span></a></h4><ul>
<li><code>text()</code>,以 string 的形式生成请求 text</li>
<li><code>json()</code>,生成 JSON.parse(responseText)的结果</li>
<li><code>blob()</code>,生成一个 Blob</li>
<li><code>arrayBuffer()</code>,生成一个 ArrayBuffer</li>
<li><code>formData()</code>,生成格式化数据,可用于其它的请求</li>
</ul></section><section><h4>其他方法<a href="#其他方法"><span>#</span></a></h4><ul>
<li><code>clone()</code>,创建一个 Response 对象的克隆</li>
<li><code>Response.error()</code>,返回一个绑定了网络错误的新的 Response 对象</li>
<li><code>Response.redirect()</code>,用另一个 URL 创建一个新的 Resonse 对象</li>
</ul></section></section></section>
<section><h2>用法<a href="#用法"><span>#</span></a></h2><section><h3>get<a href="#get"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>fetch("a.html")</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>.then(function (response) {</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>return response.text()</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>.then(function (response) {</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>document.body.innerHTML = response</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>post<a href="#post"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>fetch("a.html", {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>method: "POST",</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>headers: {</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>"Accept": "application/json",</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>"Content-Type": "application/json";</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>body: JSON.stringify({</span></div></div><div><div><div>8</div></div><div><span><span>          </span></span><span>user: "zhangsan",</span></div></div><div><div><div>9</div></div><div><span><span>          </span></span><span>pwd: 123456;</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>与原生 Ajax 的比较<a href="#与原生-ajax-的比较"><span>#</span></a></h2><section><h3>原生 Ajax<a href="#原生-ajax"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var Ajax = new XMLHttpRequest()</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Ajax.open("GET", url)</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Ajax.responseType = "json"</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>Ajax.send()</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>Ajax.onreadystatechange = function () {</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>console.log(Ajax.response)</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>Ajax.onerror = function () {</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>console.log("error")</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>fetch<a href="#fetch"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>fetch(url).then(function (response) {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>return response.json() //相当于JSON.parse(response);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}).then(function (data) {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>console.log(data)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}).catch(function (err) {</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>console.log("error", err)</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>//使用箭头函数</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>fetch(url).then(</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>response =&gt; response.json()</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>).then(</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>data =&gt; console.log(data)</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>).catch(</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>err =&gt; console.log("error", err)</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>//使用async函数</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>let Fetch = async function () {</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>try {</span></div></div><div><div><div>21</div></div><div><span><span>          </span></span><span>let response = await fetch(url)</span></div></div><div><div><div>22</div></div><div><span><span>          </span></span><span>let data = await response.json()</span></div></div><div><div><div>23</div></div><div><span><span>          </span></span><span>console.log(data)</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>} catch (err) {</span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>console.log("error", err)</span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/yuan-sheng-ajax/</id>
      <title type="text">原生Ajax</title>
      <published>2021-11-23T06:59:27.000Z</published>
      <updated>2021-11-23T06:59:27.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/yuan-sheng-ajax/"/>
      <summary type="text">Ajax 全称 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)，既是一个对象，也是一种方法模式 AJAX 是一种用于创建快速动态网页的技术，通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网…</summary>
      <content type="html"><![CDATA[<p>Ajax 全称 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)，既是一个对象，也是一种方法模式</p>
<p>AJAX 是一种用于创建快速动态网页的技术，通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下，对网页的某部分进行更新，传统的不使用 Ajax 的网页如果需要更新内容,必需重载整个网页</p>
<section><h2>用法<a href="#用法"><span>#</span></a></h2><section><h3>创建 Ajax 对象<a href="#创建-ajax-对象"><span>#</span></a></h3><p>通过 <code>XMLHttpRequest()</code>类创建一个 Ajax 对象(此方法不兼容 IE6 以下浏览器)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var Ajax=new XMLHttpRequest()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 IE6 以下浏览器通过 <code>ActiveXObject("Microsoft.XMLHTTP")</code>创建一个 Ajax 对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var Ajax=new ActiveXObject("Microsoft.XMLHTTP")</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>初始化<a href="#初始化"><span>#</span></a></h3><p>通过 <code>open()</code> 方法，可以设置跟后台交互的一些行为,该方法有三个参数
1、method；2、url；3、boolean</p><p><code>method</code> 请求方式,一个包含具体请求方式的字符串</p><ul>
<li>主要请求方式： ​
<ul>
<li>POST,建议在添加数据时使用</li>
<li>GET,建议在查询数据时使用 ​</li>
<li>PUT,建议在修改数据时使用 ​</li>
<li>DELETE,建议在删除数据时使用</li>
</ul>
</li>
</ul><p><code>url</code>,要请求数据的具体地址</p><p><code>bool</code>,进行同步或异步传输,true 为异步,false 为同步</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Ajax.open("GET", "http://localhost:8000", true)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>设置需求</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 如果想用GET的时候直接添加需求,可以在url后面加上?再写具体的需求,多个需求通过&amp;符号连接</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>var Ajax = new XMLHttpRequest()</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Ajax.open("GET","https://www.baidu.com?user=zhangsan&amp;pwd=123456",true)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// 如果是POST请求,需要设置请求头信息,如:</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>// 表单格式请求头信息的写法</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>Ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); //url格式</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>// 或</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>Ajax.setRequestHeader("Content-Type","application/json")//json格式</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>//在下一步的send()方法中传入需求</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>Ajax.send("user=zhangsan&amp;pwd=123456")</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>// 如果想用GET的时候直接添加需求,可以在url后面加上?再写具体的需求,多个需求通过&amp;符号连接</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>Ajax.open("GET","https://www.baidu.com?user=zhangsan&amp;pwd=123456",true)</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>// 如果是POST请求,需要设置请求头信息,如:</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>// 表单格式请求头信息的写法</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>Ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded") //url格式</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>// 或</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>Ajax.setRequestHeader("Content-Type","application/json")//json格式</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>//在下一步的send()方法中传入需求</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>Ajax.send("user=zhangsan&amp;pwd=123456")</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>GET 与 POST 区别</strong>
与 POST 相比，GET 更简单也更快，并且在大部分情况下都能用。
然而，在以下情况中，请使用 POST 请求：</p><ul>
<li>无法使用缓存文件（更新服务器上的文件或数据库）</li>
<li>向服务器发送大量数据（POST 没有数据量限制）</li>
<li>发送包含未知字符的用户输入时，POST 比 GET 更稳定也更可靠</li>
</ul></section><section><h3>发送请求<a href="#发送请求"><span>#</span></a></h3><p>通过 <code>send()</code>方法正式发送请求</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Ajax.send()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注: 如果请求方式是 POST 方式,添加的需求作为参数写入 <code>send()</code>方法中</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Ajax.send('a=100&amp;b=200&amp;c=300')</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>返回请求结果<a href="#返回请求结果"><span>#</span></a></h3><p>每当 <code>readyState</code> 改变时，就会触发 <code>onreadystatechange</code> 事件，因此可以给元素绑定<code>onreadystatechange</code>事件，来接受响应的事件
<code>XMLHttpRequest</code> 对象有三个非常重要的属性：<code>onreadystatechange</code>、<code>readyState</code>、<code>status</code></p><p></p><figure><img src="/images/Ajax/%E5%8E%9F%E7%94%9FAjax2.png" alt="原生Ajax2" /><figcaption>原生Ajax2</figcaption></figure><p></p><p><code>readyState</code>属性：</p><p></p><figure><img src="/images/Ajax/%E5%8E%9F%E7%94%9FAjax.png" alt="原生Ajax1" /><figcaption>原生Ajax1</figcaption></figure><p></p><p>通过 <code>status</code>(http 的状态码)属性的值判断请求后的结果从而得到请求信息,一般该值为 200 到 300 直接和等于 304 时代表请求成功,值为 404 代表请求失败</p><p><code>response</code> 属性保留着后台返回到前端的数据</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Ajax.onreadystatechange = function () {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>if (Ajax.readyState === 4) {</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>//确定http的状态码</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>if ((Ajax.status &gt;= 200 &amp;&amp; Ajax.status &lt; 300) || Ajax.status === 304) {</span></div></div><div><div><div>5</div></div><div><span><span>              </span></span><span>console.log(Ajax.response);</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>XmlHttpRequest 对象的主要方法与属性<a href="#xmlhttprequest-对象的主要方法与属性"><span>#</span></a></h2><section><h3>XmlHttpRequest 对象的主要方法<a href="#xmlhttprequest-对象的主要方法"><span>#</span></a></h3><ul>
<li>
<p><code>void open(String method,String url,Boolen async)</code>用于创建请求
参数：</p>
<ul>
<li><code>method</code>： 请求方式（字符串类型），如：POST、GET、DELETE…</li>
<li><code>url</code>： 要请求的地址（字符串类型）</li>
<li><code>async</code>： 是否异步（布尔类型）</li>
</ul>
</li>
<li>
<p><code>void send(String body)</code>用于发送请求
参数：</p>
<ul>
<li><code>body</code>： 要发送的数据（字符串类型）</li>
</ul>
</li>
<li>
<p><code>void setRequestHeader(String header,String value)</code> 用于设置请求头
参数：</p>
<ul>
<li><code>header</code>： 请求头的 key（字符串类型）</li>
<li><code>vlaue</code>： 请求头的 value（字符串类型）</li>
</ul>
</li>
<li>
<p><code>String getAllResponseHeaders()</code> 获取所有响应头
返回值：</p>
<ul>
<li>响应头数据（字符串类型）</li>
</ul>
</li>
<li>
<p><code>String getResponseHeader(String header)</code> 获取响应头中指定 header 的值
参数：</p>
<ul>
<li><code>header</code>： 响应头的 key（字符串类型）
返回值：</li>
<li>响应头中指定的 header 对应的值</li>
</ul>
</li>
<li>
<p><code>void abort()</code> 终止请求</p>
</li>
</ul></section><section><h3>XmlHttpRequest 对象的主要属性<a href="#xmlhttprequest-对象的主要属性"><span>#</span></a></h3><ul>
<li>
<p><code>Number readyState</code> 状态值（整数）
详细：</p>
<ul>
<li>0-未初始化，尚未调用 open()方法；</li>
<li>1-启动，调用了 open()方法，未调用 send()方法；</li>
<li>2-发送，已经调用了 send()方法，未接收到响应；</li>
<li>3-接收，已经接收到部分响应数据；</li>
<li>4-完成，已经接收到全部响应数据；</li>
</ul>
</li>
<li>
<p><code>Function onreadystatechange</code> 当 readyState 的值改变时自动触发执行其对应的函数（回调函数）</p>
</li>
<li>
<p><code>String responseText</code>服务器返回的数据（字符串类型）</p>
</li>
<li>
<p><code>XmlDocument responseXML</code>服务器返回的数据（Xml 对象）</p>
</li>
<li>
<p><code>Number states</code> 状态码（整数），如：200、404…</p>
</li>
<li>
<p><code>String statesText</code>状态文本（字符串），如：OK、NotFound…</p>
</li>
</ul></section></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><ol>
<li>定义 XMLHttpRequest 对象</li>
<li>初始化 设置请求方法和URL （open() 方法）</li>
<li>发送请求 （send() 方法）</li>
<li>注册监听的回调函数</li>
<li>拿到返回值，对页面进行操作</li>
</ol></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/node/</id>
      <title type="text">node</title>
      <published>2021-11-19T04:18:41.000Z</published>
      <updated>2021-11-19T04:18:41.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/node/"/>
      <summary type="text">Node是对ES标准一个实现，Node也是一个JS引擎，通过Node可以使js代码在服务器端执行，Node仅仅对ES标准进行了实现，所以在Node中不包含DOM 和 BOM</summary>
      <content type="html"><![CDATA[<section><h2>node简介<a href="#node简介"><span>#</span></a></h2><p>Node是对ES标准一个实现，Node也是一个JS引擎，通过Node可以使js代码在服务器端执行，Node仅仅对ES标准进行了实现，所以在Node中不包含DOM 和 BOM</p></section>
<section><h2>node的模块化<a href="#node的模块化"><span>#</span></a></h2><p>在Node中，一个js文件就是一个模块</p><p>每一个js文件中的js代码都是独立运行在一个函数中而不是全局作用域，所以一个模块的中的变量和函数在其他模块中无法访问</p><section><h3>CommonJS<a href="#commonjs"><span>#</span></a></h3><p>模块就是一个js文件，在模块内部任何变量或其他对象都是私有的，不会暴露给外部模块。</p><p>在CommonJS模块化规范中，在模块内部定义了一个<code>module</code>对象，<code>module</code>对象内部存储了当前模块的基本信息，同时<code>module</code>对象中有一个属性名为<code>exports</code>，<code>exports</code>用来指定需要向外部暴露的内容。只需要将需要暴露的内容设置为<code>exports</code>或<code>exports</code>的属性，其他模块即可通过<code>require</code>来获取这些暴露的内容。</p><section><h4>加载模块<a href="#加载模块"><span>#</span></a></h4><p>使用 <code>require()</code> 方法，可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。</p><p><strong>注意</strong>：使用 <code>require()</code> 方法加载其它模块时，会执行被加载模块中的代码。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 加载内置 fs 模块</span></div></div><div><div><div>2</div></div><div><span>const fs = require('fs')</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 加载自定义模块</span></div></div><div><div><div>5</div></div><div><span>const m = require('./myModule.js')</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 加载第三方模块</span></div></div><div><div><div>8</div></div><div><span>const moment = require('moment')</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 通过解构赋值按需加载模块</span></div></div><div><div><div>11</div></div><div><span>const { a, b } = require('./myModule.js')</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>module 对象<a href="#module-对象"><span>#</span></a></h4><p>在每个 .js 自定义模块中都有一个 module 对象，它里面存储了和当前模块有关的信息，打印如下</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>module</span><span>)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// output</span></div></div><div><div><div>4</div></div><div><span><span>Module</span><span> {</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>id</span><span>: </span><span>'.'</span><span>,</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>path</span><span>: </span><span>'D:</span><span>\\</span><span>study</span><span>\\</span><span>nodejs</span><span>\\</span><span>moudle'</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>exports</span><span>: {},</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>parent</span><span>: </span><span>null</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>filename</span><span>: </span><span>'D:</span><span>\\</span><span>study</span><span>\\</span><span>nodejs</span><span>\\</span><span>moudle</span><span>\\</span><span>myMoudle.js'</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>loaded</span><span>: </span><span>false</span><span>,</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>children</span><span>: [],</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>paths</span><span>: [</span></div></div><div><div><div>13</div></div><div><span>    </span><span>'D:</span><span>\\</span><span>study</span><span>\\</span><span>nodejs</span><span>\\</span><span>moudle</span><span>\\</span><span>node_modules'</span><span>,</span></div></div><div><div><div>14</div></div><div><span>    </span><span>'D:</span><span>\\</span><span>study</span><span>\\</span><span>nodejs</span><span>\\</span><span>node_modules'</span><span>,</span></div></div><div><div><div>15</div></div><div><span>    </span><span>'D:</span><span>\\</span><span>study</span><span>\\</span><span>node_modules'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>    </span><span>'D:</span><span>\\</span><span>node_modules'</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>module.exports对象<a href="#moduleexports对象"><span>#</span></a></h4><p>在自定义模块中，可以使用 <code>module.exports</code> 对象，将模块内的成员共享出去，供外界使用。</p><p>外界用 <code>require()</code> 方法 导入自定义模块时，得到的就是 <code>module.exports</code> 所指向的对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>a</span><span> </span><span>=</span><span> </span><span>10</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>b</span><span> </span><span>=</span><span> </span><span>20</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> { </span><span>name</span><span>:</span><span> </span><span>"孙悟空"</span><span> }</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>module</span><span>.</span><span>exports</span><span>.</span><span>a</span><span> </span><span>=</span><span><span> </span><span>a</span></span></div></div><div><div><div>6</div></div><div><span>module</span><span>.</span><span>exports</span><span>.</span><span>b</span><span> </span><span>=</span><span><span> </span><span>b</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// const {a, b} = require("./m.js")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>a</span><span> </span><span>=</span><span> </span><span>10</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>b</span><span> </span><span>=</span><span> </span><span>20</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> { </span><span>name</span><span>:</span><span> </span><span>"孙悟空"</span><span> }</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span><span> { </span><span>a</span><span>, </span><span>b</span><span>, </span><span>obj</span><span>}</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// const {a, b, obj} = require("./m.js")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>默认情况下，Node.js会将以下内容视为CommonJS模块：</p><ol>
<li>使用<code>.cjs</code>为扩展名的文件</li>
<li>当前的<code>package.json</code>的<code>type</code>属性为<code>commonjs</code>时，扩展名为<code>.js</code>的文件</li>
<li>当前的<code>package.json</code>不包含<code>type</code>属性时，扩展名为<code>.js</code>的文件</li>
<li>文件的扩展名是<code>mjs、cjs、json、node、js</code>以外的值时（<code>type</code>不是<code>module</code>时）</li>
</ol></section><section><h4>exports 对象<a href="#exports-对象"><span>#</span></a></h4><p>由于 <code>module.exports</code> 单词写起来比较复杂，为了简化向外共享成员的代码，Node 提供了 <code>exports</code> 对象。</p><p>默认情况 下 ， <code>exports</code> 和 <code>module.exports</code> 指向同一个对象 。最终共享的结果，还是以 <code>module.exports</code> 指向的对象为准</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>module</span><span>.</span><span>exports</span><span> </span><span>===</span><span> </span><span>exports</span><span>) </span><span>// true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const a = 10</span></div></div><div><div><div>2</div></div><div><span>const b = 20</span></div></div><div><div><div>3</div></div><div><span>const obj = { name: "孙悟空" }</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>exports.a = a</span></div></div><div><div><div>6</div></div><div><span>exports.b = b</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// const {a, b} = require("./m.js")</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>exports 和 module.exports 的使用误区<a href="#exports-和-moduleexports-的使用误区"><span>#</span></a></h4><p>时刻谨记，<code>require()</code> 模块时，得到的永远是 <code>module.exports</code> 指向的对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>exports</span><span>.</span><span>obj</span><span> </span><span>=</span><span> { </span><span>name</span><span>:</span><span> </span><span>'孙悟空'</span><span> }</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>10</span><span>,</span></div></div><div><div><div>5</div></div><div><span>  </span><span>b</span><span>:</span><span> </span><span>20</span><span>,</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// const m = require("./m.js")</span></div></div><div><div><div>9</div></div><div><span>// console.log(m) // { a: 10, b: 20 }</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>**注意：**为了防止混乱，建议不要在同一个模块中同时使用 <code>exports</code> 和 <code>module.exports</code></p></section><section><h4>文件作为模块<a href="#文件作为模块"><span>#</span></a></h4><p>当我们加载一个自定义的文件模块时，模块的路径必须以<code>/、./</code>或<code>../</code>开头。如果不以这些开头，node会认为你要加载的是核心模块或<code>node_modules</code>中的模块。</p><p>当我们要加载的模块是一个文件模块时，CommonJS规范会先去寻找该文件，比如：<code>require("./m1")</code>时，会首先寻找名为m1的文件。如果这个文件没有找到，它会自动为文件添加扩展名然后再查询。扩展名的顺序为：<code>js、json、node</code>。</p><p>还是上边的例子，如果没有找到m1，则会按照顺序搜索<code>m1.js、m1.json、m1.node</code>哪个先找到则返回哪个，如果都没有找到则报错。</p></section><section><h4>文件夹作为模块<a href="#文件夹作为模块"><span>#</span></a></h4><p>当我们使用一个文件夹作为模块时，文件夹中必须有一个模块的主文件。如果文件夹中含有<code>package.json</code>文件且文件中设置<code>main</code>属性，则<code>main</code>属性指定的文件会成为主文件，导入模块时就是导入该文件。如果没有<code>package.json</code>，则node会按照<code>index.js</code>、<code>index.node</code>的顺序寻找主文件。</p></section><section><h4>Node_modules<a href="#node_modules"><span>#</span></a></h4><p>如果我们加载的模块没有以<code>/、./</code>或<code>../</code>开头，且要加载的模块不是核心模块，node会自动去node_modules目录下去加载模块。</p><p>node会先去当前目录下的node_modules下去寻找模块，找到则使用，没找到则继续去上一层目录中寻找，以此类推，知道找到根目录下的node_modules为止。</p><p>比如，当前项目的目录为：<code>'C:\Users\lilichao\Desktop\Node-Course\myProject\node_modules'</code>，则模块查找顺序依次为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>‘C:\Users\lilichao\Desktop\Node-Course\node_modules’,</span></div></div><div><div><div>2</div></div><div><span>‘C:\Users\lilichao\Desktop\node_modules’,</span></div></div><div><div><div>3</div></div><div><span>‘C:\Users\lilichao\node_modules’,</span></div></div><div><div><div>4</div></div><div><span>‘C:\Users\node_modules’,</span></div></div><div><div><div>5</div></div><div><span>‘C:\node_modules’</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>模块的包装<a href="#模块的包装"><span>#</span></a></h4><p>每一个CommonJS模块在执行时，外层都会被套上一个函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>(</span><span>function</span><span><span>(</span><span>exports</span><span>, </span><span>require</span><span>, </span><span>module</span><span>, </span><span>__filename</span><span>, </span><span>__dirname</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 模块代码会被放到这里</span></div></div><div><div><div>3</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>所以我们之所以能在CommonJS模块中使用<code>exports</code>、<code>require</code>并不是因为它们是全局变量。它们实际上以参数的形式传递进模块的。</p><p><code>exports</code>，用来设置模块向外部暴露的内容</p><p><code>require</code>，用来引入模块的方法</p><p><code>module</code>，当前模块的引用</p><p><code>__filename</code>，模块的路径</p><p><code>__dirname</code>，模块所在目录的路径</p></section></section><section><h3>ES模块化<a href="#es模块化"><span>#</span></a></h3><p>2015年随着ES6标准的发布，ES的内置模块化系统也应运而生，并且在Node.js中同样也支持ES标准的模块化。</p><p>需要注意的是，Node.js默认并不支持ES模块化，如果需要使用可以采用两种方式。</p><p>方式一，直接将所有的<code>js</code>文件修改为<code>mjs</code>扩展名。</p><p>方式二，修改<code>package.json</code>中type属性为<code>module</code>。</p><section><h4>导出<a href="#导出"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 导出变量（命名导出）</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>let</span><span><span> </span><span>name1</span><span>, </span><span>name2</span><span>, …, </span><span>nameN</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>let</span><span><span> </span><span>name1</span><span> </span></span><span>=</span><span><span> …, </span><span>name2</span><span> </span></span><span>=</span><span><span> …, …, </span><span>nameN</span><span>;</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 导出函数（命名导出）</span></div></div><div><div><div>6</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>functionName</span><span>(){</span><span>...</span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 导出类（命名导出）</span></div></div><div><div><div>9</div></div><div><span>export</span><span> </span><span>class</span><span> </span><span>ClassName</span><span> {</span><span>...</span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 导出一组</span></div></div><div><div><div>12</div></div><div><span>export</span><span> { </span><span>name1</span><span>, </span><span>name2</span><span>, …, </span><span>nameN</span><span> }</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 重命名导出</span></div></div><div><div><div>15</div></div><div><span>export</span><span><span> { </span><span>variable1</span><span> </span><span>as</span><span> </span></span><span>name1</span><span><span>, </span><span>variable2</span><span> </span><span>as</span><span> </span></span><span>name2</span><span>, …, </span><span>nameN</span><span> }</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// 解构赋值后导出</span></div></div><div><div><div>18</div></div><div><span>export</span><span> </span><span>const</span><span> { </span><span>name1</span><span>, </span><span>name2</span><span>: </span><span>bar</span><span> } </span><span>=</span><span><span> </span><span>o</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 默认导出</span></div></div><div><div><div>21</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>expression</span></div></div><div><div><div>22</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> (…) { … } </span><span>// also class, function*</span></div></div><div><div><div>23</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>function</span><span> </span><span>name1</span><span>(…) { … } </span><span>// also class, function*</span></div></div><div><div><div>24</div></div><div><span>export</span><span><span> { </span><span>name1</span><span> </span><span>as</span><span> </span></span><span>default</span><span>, … };</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>// 聚合模块</span></div></div><div><div><div>27</div></div><div><span>export</span><span> </span><span>*</span><span> </span><span>from</span><span> …; </span><span>// 将其他模块中的全部内容导出（除了default）</span></div></div><div><div><div>28</div></div><div><span>export</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>name1</span><span> </span><span>from</span><span> …; </span><span>// ECMAScript® 2O20 将其他模块中的全部内容以指定别名导出</span></div></div><div><div><div>29</div></div><div><span>export</span><span> { </span><span>name1</span><span>, </span><span>name2</span><span>, …, </span><span>nameN</span><span> } </span><span>from</span><span> …; </span><span>// 将其他模块中的指定内容导出</span></div></div><div><div><div>30</div></div><div><span>export</span><span><span> { </span><span>import1</span><span> </span><span>as</span><span> </span></span><span>name1</span><span><span>, </span><span>import2</span><span> </span><span>as</span><span> </span></span><span>name2</span><span>, …, </span><span>nameN</span><span> } </span><span>from</span><span> …; </span><span>// 将其他模块中的指定内容重命名导出</span></div></div><div><div><div>31</div></div><div><span>export</span><span> { </span><span>default</span><span>, … } </span><span>from</span><span> …;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>引入<a href="#引入"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 引入默认导出</span></div></div><div><div><div>2</div></div><div><span>import defaultExport from "module-name"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 将所有模块导入到指定命名空间中</span></div></div><div><div><div>5</div></div><div><span>import * as name from "module-name"</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 引入模块中的指定内容</span></div></div><div><div><div>8</div></div><div><span>import { export1 } from "module-name"</span></div></div><div><div><div>9</div></div><div><span>import { export1 , export2 } from "module-name"</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 以指定别名引入模块中的指定内容</span></div></div><div><div><div>12</div></div><div><span>import { export1 as alias1 } from "module-name"</span></div></div><div><div><div>13</div></div><div><span>import { export1 , export2 as alias2 , [...] } from "module-name"</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 引入默认和其他内容</span></div></div><div><div><div>16</div></div><div><span>import defaultExport, { export1 [ , [...] ] } from "module-name"</span></div></div><div><div><div>17</div></div><div><span>import defaultExport, * as name from "module-name"</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 引入模块</span></div></div><div><div><div>20</div></div><div><span>import "module-name"</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/less/</id>
      <title type="text">less</title>
      <published>2021-11-16T05:13:01.000Z</published>
      <updated>2021-11-16T05:13:01.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/less/"/>
      <summary type="text">less 是一种动态样式语言,属于 CSS 预处理器的范畴,它拓展了 CSS 语言 ,其余的 CSS 预处理器有 sass 和 stylus 等 less 增加了变量,MIxin,函数等特性,使 CSS 更易维护和扩展,less 既可以在客户端上运行,也可以借助 node.js 在服务端 运行</summary>
      <content type="html"><![CDATA[<p><strong>less 是一种动态样式语言,属于 CSS 预处理器的范畴,它拓展了 CSS 语言</strong>,其余的 CSS 预处理器有 sass 和 stylus 等 less 增加了变量,MIxin,函数等特性,使 CSS 更易维护和扩展,less 既可以在客户端上运行,也可以借助 node.js 在服务端 运行</p>
<section><h2>less 的注释<a href="#less-的注释"><span>#</span></a></h2><p>less 中的注释和 CSS 一样有两种单行注释 <code>//</code>，多行注释 <code>/**/</code></p><p>区别：以 <code>//</code> 开头的注释不会被编译到 CSS 文件中，而通过 <code>/**/</code> 包裹的注释才会被编译在 CSS 文件中</p></section>
<section><h2>less 的变量<a href="#less-的变量"><span>#</span></a></h2><ul>
<li>less 中使用<code>@</code>来申明和使用一个变量，所有变量声明方法相同</li>
</ul><p>具体用法为: <code>@变量名:属性值</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>@pink:pink //在以后的使用中就可以用@pink 来代替 pink</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>变量作为<strong>普通属性值</strong>使用，直接用这个变量:<code>@变量名</code>
变量作为<strong>选择器或者属性名</strong>来使用，需要在变量两边加上{}，<code>{selector}</code>
变量作为<code> url</code>，使用是也需要在两边加上{}: <code>{url}</code></p><p>变量的加载会有延迟效果,当整个文件的所有东西被加载完时才会将变量赋值给对应的属性</p><p>变量运用的时候会现在自身的作用域里面找到是否定义了该变量,如果定义了就直接用自身作用域中的,如果没有才会在外层逐层寻找,如果在最外层都没有找到对应的变量就会报错</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>@var: 0;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>.class {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>@var: 1;</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>.brass {</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>@var: 2;</span></div></div><div><div><div>7</div></div><div><span><span>          </span></span><span>width: @var;</span></div></div><div><div><div>8</div></div><div><span><span>          </span></span><span>@var: 3;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>height: @var;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>/*</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>最终的结果为  height:1;  width:3;</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>变量在最后赋完所有值才会赋值给属性,</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>而且这个赋值也是只针对自身的作用域</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>*/</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>less 的导入<a href="#less-的导入"><span>#</span></a></h2><p>在 less 中使用@import 关键字能够将需要的文件中的代码导入到当前的 less 文件中</p><ul>
<li>
<p>导入 less 文件可一直接导入</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>@import "a.less";</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>导入 CSS 文件时需要申明这是 CSS 文件</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>@import (CSS) "b.css";</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section>
<section><h2>嵌套规则<a href="#嵌套规则"><span>#</span></a></h2><section><h3>基本嵌套规则<a href="#基本嵌套规则"><span>#</span></a></h3><ul>
<li>
<p>less 中可以在父选择器中包含有子选择器,在转换为 CSS 文件的时候会自动用空格来隔开</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.father {</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>.child {</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>/*</span></div></div><div><div><div>9</div></div><div><span><span>       </span></span><span>通过上方的写法可以分别对父元素和子元素进行样式设置</span></div></div><div><div><div>10</div></div><div><span><span>     </span></span><span>*/</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>&amp;符号的使用<a href="#符号的使用"><span>#</span></a></h3><p><strong>&amp;符号能取消默认的嵌套关系的中的空格</strong>，从而从父子关系变成兄弟关系，这个兄弟关系是本身拓展出的一些兄弟关系,如伪类选择器等，可以理解为 &amp; 符号就是这个选择器本身</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.father {</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>.child {</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>background-color: red;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>&amp;:hover {</span></div></div><div><div><div>7</div></div><div><span><span>              </span></span><span>background-color: pink;</span></div></div><div><div><div>8</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>//相当于</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>.father .child {</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>background-color: red;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>.father .child:hover {</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>background-color: pink;</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h2>less 的运算<a href="#less-的运算"><span>#</span></a></h2><p>在 less 中可以进行加减乘除的运算</p><p>注意&lt;在&gt; less 的计算中计算的双方只需要一方带单位,最后再计算的时候单位会保留下来加在新值的后面</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.box {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>widht: (100+100px);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>less 的混合<a href="#less-的混合"><span>#</span></a></h2><p><strong>混合就是将一系列属性从一个规则集引入到另外一个规则集的方式</strong>，相当于直接复制</p><section><h3>普通混合<a href="#普通混合"><span>#</span></a></h3><p>把样式中共同的代码单独提出来在最上方,单独的用一个类名来定义,然后在下面输入的时候通过 <code>.类名/;</code> 来调用</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.mix {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>//不加()是普通混合,会被编译到CSS文件中</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>width: 100px;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>height: 100px;</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>background-color: red;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>.father {</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>width: 200px;</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>height: 200px;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>.child1 {</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>.mix; //调用混合样式</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>.child2 {</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>.mix;</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>不带输出混合<a href="#不带输出混合"><span>#</span></a></h3><p>不带输出混合与普通混合最大的区别就是：不带输出混合不会编译到 css 文件中，而普通混合则会</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.mix() {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>//不加()是普通混合,会被编译到CSS文件中</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>width: 100px;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>height: 100px;</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>background-color: red;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>.father {</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>width: 200px;</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>height: 200px;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>.child1 {</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>.mix(); //调用混合样式</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>.child2 {</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>.mix();</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>带参数的混合<a href="#带参数的混合"><span>#</span></a></h3><p>混合的参数也是通过变量使用的形式来定义的，混合中加参数的用法和函数类似，为了方便记忆可以理解为函数，但是不是函数，同时这个参数还可以有默认的值，默认值的定义也是和变量的定义相同</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.mix(@w: 10px, @h: 10px, @c: pink) {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>width: @w;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>height: @h;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>background-color: @c;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>.father {</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>width: 200px;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>height: 200px;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>.child1 {</span></div></div><div><div><div>12</div></div><div><span><span>          </span></span><span>.mix(100px, 100px, red);</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>.child2 {</span></div></div><div><div><div>16</div></div><div><span><span>          </span></span><span>.mix(50px, 50px, black);</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果想对上述单个属性赋值，而其他属性使用默认值的话，可使用命名参数混合</p></section><section><h3>命名参数混合<a href="#命名参数混合"><span>#</span></a></h3><p>在实参传递给形参值的时候可以不用按照顺序进行传递，而是直接通过参数的指定进行传参，这样就能够实现当只想传递某些参数而不是所有参数的时候参数的值不产生混乱</p><p>实参传入命名参数的时候和变量定义的时候是一样的</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.mix(@w: 10px, @h: 10px, @c: pink) {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>width: @w;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>height: @h;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>background-color: @c;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>.father {</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>width: 200px;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>height: 200px;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>.child1 {</span></div></div><div><div><div>12</div></div><div><span><span>          </span></span><span>.mix(100px, 100px, red);</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>.child2 {</span></div></div><div><div><div>16</div></div><div><span><span>          </span></span><span>.mix(@c:black);</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>匹配模式<a href="#匹配模式"><span>#</span></a></h3><p>用一些专门的标识符放在形参的首位用做匹配，注意<strong>这个是标识符不用@符号</strong>，当使用的时候会根据传入的第一个参数，匹配到那个，就使用哪一个样式</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.float(l, @w) {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>float: left;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>width: @w;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>.float(r, @w) {</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>float: right;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>width: @w;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>.box1 {</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>height: 200px;</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>background: green;</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>.float(l, 200px);</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>.box2 {</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>width: 300px;</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>height: 200px;</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>background: green;</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>.float(r, 200px);</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>arguments 变量<a href="#arguments-变量"><span>#</span></a></h3><p><strong>arguments 相当于是类中所有实参的集合</strong>，其作用和 js 中的 arguments 类似
这个变量是 less 已经设置了的,可以直接调用它来使用,调用它的时候它包含了所有传入的实参,同时每个实参是通过空格隔开的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.border(@size,@style,@color) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>border: @arguments;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>div {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>width: 200px;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>height: 200px;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>.border(1px, solid, #000);</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意&lt;和&gt; js 中不一样的是这个属性虽然保留着所有实参但是在命名类的时候还是需要写入对应的形参，不然会出现报错</p></section></section>
<section><h2>less 的继承<a href="#less-的继承"><span>#</span></a></h2><p>less 的继承性能上高于 less 的混合,但是灵活性弱于混合,继承不支持参数的形式,同时后面也不加(),继承的类是需要被编译到 CSS 文件中的</p><p>less 的继承用关键词 <code>&amp;:extend()</code> 函数来实现，下面是一种用法</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.jicheng{</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>width: 100px;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>height: 100px;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>background: red;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>.box:extend(.jicheng){</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>border: solid 1px black;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>// 相当于</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>.jicheng,</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>.box {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>width: 100px;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>height: 100px;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>background: red;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>.box {</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>border: solid 1px black;</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>同时也可以继承所有状态（如伪类选择器），<code>name:extend（继承类名 all）{}</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>.jicheng{</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>width: 100px;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>height: 100px;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>background: red;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>.jicheng:hover{</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>background: black;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>.box:extend(.jicheng){</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>border: solid 1px black;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>.box2:extend(.jicheng all){</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>border: solid 1px black;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>//相当于</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>.jicheng,</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>.box,</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>.box2 {</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>width: 100px;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>height: 100px;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>background: red;</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>.jicheng:hover,</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>.box2:hover {</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>background: black;</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>.box {</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>border: solid 1px black;</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>.box2 {</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>border: solid 1px black;</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>less 的避免编译<a href="#less-的避免编译"><span>#</span></a></h2><p>在 less 中如果想要一些值不被运算想要原封不动的变成 CSS 文件,可以用 <code>~"值"</code>的方式直接将值原封传递给 CSS 文件</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>* {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>margin: ~"calc(100px + 100)"; //calc()是CSS中本来就有的计算形式的函数</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/symbol/</id>
      <title type="text">Symbol</title>
      <published>2021-11-11T23:08:35.000Z</published>
      <updated>2021-11-11T23:08:35.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/symbol/"/>
      <summary type="text">ES5 的对象属性名都是字符串，这容易造成属性名的冲突。如在对象中添加一个方法，这个方法名有可能导致重名，引起冲突 ES6 引入了一种新的原始数据类型 Symbol ，表示独一无二的值。它是 JavaScript 语言的第七种数据类型 对象的属性名现在可以有两种类型，一种是原来就有的字符串，另一种就…</summary>
      <content type="html"><![CDATA[<p>ES5 的对象属性名都是字符串，这容易造成属性名的冲突。如在对象中添加一个方法，这个方法名有可能导致重名，引起冲突</p>
<p>ES6 引入了一种新的原始数据类型 <code>Symbol</code>，表示独一无二的值。它是 JavaScript 语言的第七种数据类型</p>
<p>对象的属性名现在可以有两种类型，一种是原来就有的字符串，另一种就是新增的 Symbol 类型。凡是属性名属于 <code>Symbol</code> 类型，就都是独一无二的，可以保证不会与其他属性名产生冲突</p>
<section><h2>基本使用<a href="#基本使用"><span>#</span></a></h2><p>声明方式:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s = Symbol()</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(s, typeof s) // Symbol() 'symbol'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Symbol</code> 函数前不能使用 <code>new</code> 命令，否则会报错。这是因为生成的 Symbol 是一个原始类型的值，不是对象。也就是说，由于 <code>Symbol</code> 值不是对象，所以不能添加属性。基本上，它是一种类似于字符串的数据类型</p><p><code>Symbol</code>函数可以接受一个字符串作为参数，表示对 <code>Symbol</code> 实例的描述，主要是为了在控制台显示，或者转为字符串时，比较容易区分</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s1 = Symbol("s1");</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let s2 = Symbol("s2");</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console.log(s1) // Symbol(s1)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log(s2) // Symbol(s2)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>console.log(s1.toString()) // "Symbol(s1)"</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console.log(s2.toString()) // "Symbol(s2)"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果 <code>Symbol</code> 的参数是一个对象，就会调用该对象的<code>toString()</code>方法，将其转为字符串，然后才生成一个 <code>Symbol</code> 值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>name: "obj",</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>toString: function() {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>return this.name</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>let s = Symbol(obj)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console.log(s) //Symbol(obj)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注，<code>Symbol</code>函数的参数只是表示对当前 <code>Symbol</code> 值的描述，因此相同参数的<code>Symbol</code>函数的返回值是不相等的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 没有参数的情况</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let s1 = Symbol()</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>let s2 = Symbol()</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>s1 === s2 // false</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>// 有参数的情况</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>let s1 = Symbol("s")</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>let s2 = Symbol("s")</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>s1 === s2 // false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Symbol</code> 值不能与其他类型的值进行运算和比较，会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let s = Symbol("s")</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let result = s + 100</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>// TypeError: Cannot convert a Symbol value to a number</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Symbol.for()<a href="#symbolfor"><span>#</span></a></h2><p>有时，我们希望重新使用同一个 Symbol 值，<code>Symbol.for()</code>方法可以做到这一点。它接受一个字符串作为参数，然后搜索有没有以该参数作为名称的 <code>Symbol</code> 值。如果有，就返回这个 <code>Symbol</code> 值，否则就新建并返回一个以该字符串为名称的 <code>Symbol</code> 值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Symbol.for("foo") // 创建一个 symbol 并放入 symbol 注册表中，键为 "foo"</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Symbol.for("foo") // 从 symbol 注册表中读取键为"foo"的 symbol</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>Symbol.for("foo") === Symbol.for("foo") // true</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>Symbol("bar") === Symbol("bar") // false，Symbol() 函数每次都会返回新的一个 symbol</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>var sym = Symbol.for("mario")</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>sym.toString()</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>// "Symbol(mario)"，mario 既是该 symbol 在 symbol 注册表中的键名，又是该 symbol 自身的描述字符串</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Symbol.for()</code>与<code>Symbol()</code>这两种写法，都会生成新的 <code>Symbol</code>。它们的区别是，前者会被登记在全局环境中供搜索，后者不会。<code>Symbol.for()</code>不会每次调用就返回一个新的 <code>Symbol</code> 类型的值，而是会先检查给定的key是否已经存在，如果不存在才会新建一个值</p></section>
<section><h2>Symbol.keyFor()<a href="#symbolkeyfor"><span>#</span></a></h2><p><code>Symbol.keyFor()</code>方法返回一个已登记的 <code>Symbol</code> 类型值的 key</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let s1 = Symbol.for("foo")</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>Symbol.keyFor(s1) // "foo"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>let s2 = Symbol("foo")</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>Symbol.keyFor(s2) // undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>变量<code>s2</code>属于未登记的 <code>Symbol</code> 值，所以返回<code>undefined</code></p><p>需要注意的是，<code>Symbol.for()</code>为 Symbol 值登记的名字，是全局环境的，可以在不同的 iframe 或 service worker 中取到同一个值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>iframe = document.createElement("iframe")</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>iframe.src = String(window.location)</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>document.body.appendChild(iframe)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>iframe.contentWindow.Symbol.for("foo") === Symbol.for("foo")  // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>内置的 Symbol 值<a href="#内置的-symbol-值"><span>#</span></a></h2><p>除了定义自己使用的 Symbol 值以外，ES6 还提供了 11 个内置的 Symbol 值，指向语言内部使用的方法</p><section><h3>Symbol.hasInstance<a href="#symbolhasinstance"><span>#</span></a></h3><p>对象的<code>Symbol.hasInstance</code>属性，指向一个内部方法。当其他对象使用<code>instanceof</code>运算符，判断是否为该对象的实例时，会调用这个方法。比如，<code>foo instanceof Foo</code>在语言内部，实际调用的是<code>Foo[Symbol.hasInstance](foo)</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class MyClass {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>[Symbol.hasInstance](foo) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>return foo instanceof Array;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>[1, 2, 3] instanceof new MyClass(); // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，<code>MyClass</code>是一个类，<code>new MyClass()</code>会返回一个实例。该实例的<code>Symbol.hasInstance()</code>方法，会在进行<code>instanceof</code>运算时自动调用，判断左侧的运算子是否为<code>Array</code>的实例</p></section><section><h3>Symbol.isConcatSpreadable<a href="#symbolisconcatspreadable"><span>#</span></a></h3><p>对象的<code>Symbol.isConcatSpreadable</code>属性等于一个布尔值，表示该对象用于<code>Array.prototype.concat()</code>时，是否可以展开</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let arr1 = ["c", "d"];</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>["a", "b"].concat(arr1, "e"); // ['a', 'b', 'c', 'd', 'e']</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>arr1[Symbol.isConcatSpreadable]; // undefined</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>let arr2 = ["c", "d"];</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>arr2[Symbol.isConcatSpreadable] = false;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>["a", "b"].concat(arr2, "e"); // ['a', 'b', ['c','d'], 'e']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码说明，数组的默认行为是可以展开，<code>Symbol.isConcatSpreadable</code>默认等于<code>undefined</code>。该属性等于<code>true</code>时，也有展开的效果</p><p>类似数组的对象正好相反，默认不展开。它的<code>Symbol.isConcatSpreadable</code>属性设为<code>true</code>，才可以展开</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let obj = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>length: 2,</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>0: "c",</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>1: "d"</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>["a", "b"].concat(obj, "e"); // ['a', 'b', obj, 'e']</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>obj[Symbol.isConcatSpreadable] = true;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>["a", "b"].concat(obj, "e"); // ['a', 'b', 'c', 'd', 'e']</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Symbol.species<a href="#symbolspecies"><span>#</span></a></h3><p>对象的<code>Symbol.species</code>属性，指向一个构造函数。创建衍生对象时，会使用该属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class MyArray extends Array {}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const a = new MyArray(1, 2, 3);</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>const b = a.map((x) =&gt; x);</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>const c = a.filter((x) =&gt; x &gt; 1);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>b instanceof MyArray; // true</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>c instanceof MyArray; // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，子类<code>MyArray</code>继承了父类<code>Array</code>，a是<code>MyArray</code>的实例，b和c是a的衍生对象。您可能会认为，b和c都是调用数组方法生成的，所以应该是数组(<code>Array</code>的实例)，但实际上它们也是<code>MyArray</code>的实例</p></section><section><h3>Symbol.match<a href="#symbolmatch"><span>#</span></a></h3><p>对象的<code>Symbol.match</code>属性，指向一个函数。当执行<code>str.match(myObject)</code>时，如果该属性存在，会调用它，返回该方法的返回值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.prototype.match(regexp);</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>regexp[Symbol.match](this);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>class MyMatcher {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>[Symbol.match](string) {</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>return "hello world".indexOf(string);</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>"e".match(new MyMatcher()); // 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Symbol.replace<a href="#symbolreplace"><span>#</span></a></h3><p>对象的<code>Symbol.replace</code>属性，指向一个方法，当该对象被<code>String.prototype.replace</code>方法调用时，会返回该方法的返回值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.prototype.replace(searchValue, replaceValue);</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>searchValue[Symbol.replace](this, replaceValue);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const x = {};</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>x[Symbol.replace] = (...s) =&gt; console.log(s);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>"Hello".replace(x, "World"); // ["Hello", "World"]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>Symbol.replace</code>方法会收到两个参数，第一个参数是<code>replace</code>方法正在作用的对象，上面例子是<code>Hello</code>，第二个参数是替换后的值，上面例子是<code>World</code></p></section><section><h3>Symbol.search<a href="#symbolsearch"><span>#</span></a></h3><p>对象的<code>Symbol.search</code>属性，指向一个方法，当该对象被<code>String.prototype.search</code>方法调用时，会返回该方法的返回值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String.prototype.search(regexp);</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// 等同于</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>regexp[Symbol.search](this);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>class MySearch {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>constructor(value) {</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>this.value = value;</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>[Symbol.search](string) {</span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>return string.indexOf(this.value);</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>"foobar".search(new MySearch("foo")); // 0</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Symbol.split<a href="#symbolsplit"><span>#</span></a></h3><p>对象的<code>Symbol.split</code>属性，指向一个方法，当该对象被<code>String.prototype.split</code>方法调用时，会返回该方法的返回值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>String.prototype.split(separator, limit);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>separator[Symbol.split](this, limit);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>例：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class MySplitter {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>constructor(value) {</span></div></div><div><div><div>3</div></div><div><span><span>                </span></span><span>this.value = value;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>[Symbol.split](string) {</span></div></div><div><div><div>6</div></div><div><span><span>                </span></span><span>let index = string.indexOf(this.value);</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>if (index === -1) {</span></div></div><div><div><div>8</div></div><div><span><span>                    </span></span><span>return string;</span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>                </span></span><span>return [string.substr(0, index), string.substr(index + this.value.length)];</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>"foobar".split(new MySplitter("foo"));</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>// ['', 'bar']</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>"foobar".split(new MySplitter("bar"));</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>// ['foo', '']</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>"foobar".split(new MySplitter("baz"));</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>// 'foobar'</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面方法使用<code>Symbol.split</code>方法，重新定义了字符串对象的split方法的行为</p></section><section><h3>Symbol.iterator<a href="#symboliterator"><span>#</span></a></h3><p>对象的<code>Symbol.iterator</code>属性，指向该对象的默认遍历器方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const myIterable = {};</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>myIterable[Symbol.iterator] = function * () {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>yield 1;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>yield 2;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>yield 3;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>[...myIterable]; // [1, 2, 3]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对象进行<code>for...of</code>循环时，会调用<code>Symbol.iterator</code>方法，返回该对象的默认遍历器</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>class Collection {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>*[Symbol.iterator]() {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>let i = 0;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>while (this[i] !== undefined) {</span></div></div><div><div><div>5</div></div><div><span><span>                </span></span><span>yield this[i];</span></div></div><div><div><div>6</div></div><div><span><span>                </span></span><span>++i;</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>let myCollection = new Collection();</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>myCollection[0] = 1;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>myCollection[1] = 2;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>for (let value of myCollection) {</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>console.log(value);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>// 1</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>// 2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Symbol.toPrimitive<a href="#symboltoprimitive"><span>#</span></a></h3><p>对象的<code>Symbol.toPrimitive</code>属性，指向一个方法。该对象被转为原始类型的值时，会调用这个方法，返回该对象对应的原始类型值。
<code>Symbol.toPrimitive</code>被调用时，会接受一个字符串参数，表示当前运算的模式，一共有三种模式。</p><ul>
<li>
<p>Number: 该场合需要转成数值</p>
</li>
<li>
<p>String: 该场合需要转成字符串</p>
</li>
<li>
<p>Default: 该场合可以转成数值，也可以转成字符串</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let obj = {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>[Symbol.toPrimitive](hint) {</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>switch (hint) {</span></div></div><div><div><div>4</div></div><div><span><span>              </span></span><span>case "number":</span></div></div><div><div><div>5</div></div><div><span><span>                  </span></span><span>return 123;</span></div></div><div><div><div>6</div></div><div><span><span>              </span></span><span>case "string":</span></div></div><div><div><div>7</div></div><div><span><span>                  </span></span><span>return "str";</span></div></div><div><div><div>8</div></div><div><span><span>              </span></span><span>case "default":</span></div></div><div><div><div>9</div></div><div><span><span>                  </span></span><span>return "default";</span></div></div><div><div><div>10</div></div><div><span><span>              </span></span><span>default:</span></div></div><div><div><div>11</div></div><div><span><span>                  </span></span><span>throw new Error();</span></div></div><div><div><div>12</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>2 * obj; // 246</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>3 + obj; // '3default'</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>obj == "default"; // true</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>String(obj); // 'str'</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section><section><h3>Symbol.toStringTag<a href="#symboltostringtag"><span>#</span></a></h3><p>对象的<code>Symbol.toStringTag</code>属性，指向一个方法。在该对象上面调用<code>Object.prototype.toString</code>方法时，如果这个属性存在，它的返回值会出现在<code>toString()</code>方法返回的字符串之中，表示对象的类型。也就是说，这个属性可以用来定制<code>[object Object]</code>或<code>[object Array]</code>中<code>object</code>后面的那个字符串</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 例一</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>({</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>[Symbol.toStringTag]: "Foo"</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}.toString());</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// "[object Foo]"</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// 例二</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>class Collection {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>get[Symbol.toStringTag]() {</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>return "xxx";</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>let x = new Collection();</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>Object.prototype.toString.call(x); // "[object xxx]"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>ES6 新增内置对象的<code>Symbol.toStringTag</code>属性值如下。</p><ul>
<li><code>JSON[Symbol.toStringTag]</code>: ‘JSON’</li>
<li><code>Math[Symbol.toStringTag]</code>: ‘Math’</li>
<li>Module 对象 <code>M[Symbol.toStringTag]</code>: ‘Module’</li>
<li><code>ArrayBuffer.prototype[Symbol.toStringTag]</code>: ‘ArrayBuffer’</li>
<li><code>DataView.prototype[Symbol.toStringTag]</code>: ‘DataView’</li>
<li><code>Map.prototype[Symbol.toStringTag]</code>: ‘Map’</li>
<li><code>Promise.prototype[Symbol.toStringTag]</code>: ‘Promise’</li>
<li><code>Set.prototype[Symbol.toStringTag]</code>: ‘Set’</li>
<li><code>%TypedArray%.prototype[Symbol.toStringTag]</code>: ‘Uint8Array’等</li>
<li><code>WeakMap.prototype[Symbol.toStringTag]</code>: ‘WeakMap’</li>
<li><code>WeakSet.prototype[Symbol.toStringTag]</code>: ‘WeakSet’</li>
<li><code>%MapIteratorPrototype%[Symbol.toStringTag]</code>: ‘Map Iterator’</li>
<li><code>%SetIteratorPrototype%[Symbol.toStringTag]</code>: ‘Set Iterator’</li>
<li><code>%StringIteratorPrototype%[Symbol.toStringTag]</code>: ‘String Iterator’</li>
<li><code>Symbol.prototype[Symbol.toStringTag]</code>: ‘Symbol’</li>
<li><code>Generator.prototype[Symbol.toStringTag]</code>: ‘Generator’</li>
<li><code>GeneratorFunction.prototype[Symbol.toStringTag]</code>: ‘GeneratorFunction</li>
</ul></section><section><h3>Symbol.unscopables<a href="#symbolunscopables"><span>#</span></a></h3><p>对象的<code>Symbol.unscopables</code>属性，指向一个对象。该对象指定了使用<code>with</code>关键字时，哪些属性会被<code>with</code>环境排除</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>Array.prototype[Symbol.unscopables];</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>// {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>//   copyWithin: true,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>//   entries: true,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>//   fill: true,</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>//   find: true,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>//   findIndex: true,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>//   includes: true,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>//   keys: true</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// }</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>Object.keys(Array.prototype[Symbol.unscopables]);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>// ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码说明，数组有 7 个属性，会被with命令排除</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 没有 unscopables 时</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>class MyClass {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>foo() {</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>return 1;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>var foo = function () {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>return 2;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>with(MyClass.prototype) {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>foo(); // 1</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>// 有 unscopables 时</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>class MyClass {</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>foo() {</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>return 1;</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>get[Symbol.unscopables]() {</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>return {</span></div></div><div><div><div>23</div></div><div><span><span>                </span></span><span>foo: true</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>};</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>var foo = function () {</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>return 2;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>with(MyClass.prototype) {</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>foo(); // 2</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上面代码通过指定<code>Symbol.unscopables</code>属性，使得<code>with</code>语法块不会在当前作用域寻找<code>foo</code>属性，即<code>foo</code>将指向外层作用域的变量</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/han-shu-de-kuo-zhan/</id>
      <title type="text">函数的扩展</title>
      <published>2021-11-09T05:57:08.000Z</published>
      <updated>2021-11-09T05:57:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/han-shu-de-kuo-zhan/"/>
      <summary type="text">ES6 允许给函数参数赋值初始值</summary>
      <content type="html"><![CDATA[<section><h2>函数参数的默认值<a href="#函数参数的默认值"><span>#</span></a></h2><p>ES6 允许给函数参数赋值初始值</p><section><h3>基本用法<a href="#基本用法"><span>#</span></a></h3><p>通过给形参赋初始值，具有默认值的参数, 一般位置要靠后(潜规则)，<strong>只有当传入的参数的值为<code>undefined</code>时才会使用默认值</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function log(x, y = "World") {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(x, y)</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>log("Hello") // Hello World</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>log("Hello", "China") // Hello China</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>log("Hello", "") // Hello</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用参数默认值时，函数不能有同名参数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 不报错，但是先传入的同名参数会被后面覆盖</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>function fun(x, x, y) {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>function fun(x, x, y = 1) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>// ...</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>// SyntaxError: Duplicate parameter name not allowed in this context</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>与解构赋值默认值结合使用<a href="#与解构赋值默认值结合使用"><span>#</span></a></h3><p>参数默认值可以与解构赋值的默认值，结合起来使用</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function fun({ x, y = 5 }) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(x, y);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>fun({}); // undefined 5</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>fun({ x: 1 }); // 1 5</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>fun({ x: 1, y: 2 }); // 1 2</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>fun(); // TypeError: Cannot read property 'x' of undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码只使用了对象的解构赋值默认值，没有使用函数参数的默认值</p><p>只有当函数<code>fun</code>的参数是一个对象时，变量<code>x</code>和<code>y</code>才会通过解构赋值生成。如果函数<code>fun</code>调用时没提供参数，变量<code>x</code>和<code>y</code>就不会生成，从而报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function fun({ x, y = 5 } = {}) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(x, y)</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>fun() // undefined 5</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码指定，如果没有提供参数，函数<code>fun</code>的参数默认为一个空对象</p><p>例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function fetch(url, { body = "", method = "GET", headers = {} }) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(method);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>fetch("https://example.com", {});</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// "GET"</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>fetch("https://example.com");</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>// 报错</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，如果函数<code>fetch</code>的第二个参数是一个对象，就可以为它的三个属性设置默认值。
这种写法不能省略第二个参数，如果结合函数参数的默认值，就可以省略第二个参数。这时，就出现了双重默认值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function fetch(url, { body = "", method = "GET", headers = {} } = {}) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(method);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>fetch("https://example.com");</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// "GET"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，函数<code>fetch</code>没有第二个参数时，函数参数的默认值就会生效，然后才是解构赋值的默认值生效，变量<code>method</code>才会取到默认值<code>GET</code></p><p>问：以下代码有何区别</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 写法一</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>function m1({ x = 0, y = 0 } = {}) {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>return [x, y]</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// 写法二</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>function m2({ x, y } = { x: 0, y: 0 }) {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>return [x, y]</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面两种写法都对函数的参数设定了默认值，区别是写法一函数参数的默认值是空对象，但是设置了对象解构赋值的默认值
写法二函数参数的默认值是一个有具体属性的对象，但是没有设置对象解构赋值的默认值</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 函数没有参数的情况</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>m1() // [0, 0]</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>m2() // [0, 0]</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>// x 和 y 都有值的情况</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>m1({ x: 3, y: 8 }) // [3, 8]</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>m2({ x: 3, y: 8 }) // [3, 8]</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>// x 有值，y 无值的情况</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>m1({ x: 3 }) // [3, 0]</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>m2({ x: 3 }) // [3, undefined]</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>// x 和 y 都无值的情况</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>m1({}) // [0, 0];</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>m2({}) // [undefined, undefined]</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>函数的 length 属性<a href="#函数的-length-属性"><span>#</span></a></h3><p>指定了默认值以后，函数的<code>length</code>属性，将返回没有指定默认值的参数个数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>(function (a) {}).length // 1</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>(function (a = 5) {}).length // 0</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>(function (a, b, c = 5) {}).length // 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因为<code>length</code>属性的含义是，该函数预期传入的参数个数。某个参数指定默认值以后，预期传入的参数个数就不包括这个参数了</p><p>如果设置了默认值的参数不是尾参数，那么<code>length</code>属性也不再计入后面的参数了</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>(function (a, b = 1, c) {}).length  // 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>作用域<a href="#作用域"><span>#</span></a></h3><p>一旦设置了参数的默认值，函数进行声明初始化时，参数会形成一个单独的作用域(context)。等到初始化结束，这个作用域就会消失</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let x = 1</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>function fun(x, y = x) {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>console.log(y)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>fun(2) // 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，参数<code>y</code>的默认值等于<code>x</code>，调用<code>fun()</code>时，此时形成了封闭的作用域，默认值<code>x</code>指向第一个参数<code>x</code>，也就是2</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let x = 1</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>function fun(y = x) {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>let x = 3</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>console.log(y)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>fun() // 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，函数f调用时，参数<code>y = x</code>形成一个单独的作用域。这个作用域里面，变量<code>x</code>本身没有定义，所以指向外层的全局变量<code>x</code>。函数调用时，函数体内部的局部变量<code>x</code>影响不到默认值变量<code>x</code>。</p><p>如果此时，全局变量<code>x</code>不存在，就会报错</p></section></section>
<section><h2>rest 参数<a href="#rest-参数"><span>#</span></a></h2><p>ES6 引入 <code>rest</code> 参数(形式为…变量名)，用于获取函数的多余参数，这样就不需要使用<code>arguments</code>对象了。
<code>rest</code> 参数搭配的变量是一个数组，该变量将多余的参数放入数组中</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// ES5 获取实参的方式</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>function fun(){</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>console.log(arguments)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>fun('a','b','c')</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// rest 参数</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>function fun(x, ...args){</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console.log(args);// filter some every map</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>fun('a','b','c') // b c</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>rest</code> 参数必须要放到参数最后，否则会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function fun(a, ...args, b) {}</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// SyntaxError: Rest parameter must be last formal parameter</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>函数的<code>length</code>属性，不包括 <code>rest</code> 参数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>(function (...args) {}).length // 0</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>(function (a, b, ...args) {}).length // 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>箭头函数<a href="#箭头函数"><span>#</span></a></h2><p>ES6 允许使用“箭头”(<code>=&gt;</code>)定义函数</p><section><h3>基本用法<a href="#基本用法-1"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let fun = function (a) {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return a * a</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>// 等价于</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>let fun = (a) =&gt; {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>return a * a</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果箭头函数当形参有且只有一个的时候，可以省略<code>()</code>
当代码体只有一条语句的时候, 可以省略<code>{}</code>，但此时 <code>return</code> 必须省略，<code>return</code> 返回该条语句执行结果</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let fun = (a) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>return a * a</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>//等价于</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>let fun = a =&gt; a * a</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>由于大括号被解释为代码块，所以如果箭头函数直接返回一个对象，必须在对象外面加上括号，否则会报错</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 报错</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let getTempItem = id =&gt; { id: id, name: "Temp" }</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>// 不报错</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>let getTempItem = id =&gt; ({ id: id, name: "Temp" })</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>注意事项<a href="#注意事项"><span>#</span></a></h3><ul>
<li>
<p><code>this</code> 是静态的. <code>this</code> 始终指向函数声明时所在作用域下的 <code>this</code> 的值</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>function getName() {</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>console.log(this.name)</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span> </span></span><span>let getName2 = () =&gt; {</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>console.log(this.name)</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span> </span></span><span>window.name = "window"</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>const obj = {</span></div></div><div><div><div>11</div></div><div><span><span>     </span></span><span>name: "obj"</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span> </span></span><span>// 直接调用</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>getName() //window</span></div></div><div><div><div>16</div></div><div><span><span> </span></span><span>getName2() //window</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span><span> </span></span><span>// call 方法调用</span></div></div><div><div><div>19</div></div><div><span><span> </span></span><span>getName.call(obj) //obj</span></div></div><div><div><div>20</div></div><div><span><span> </span></span><span>getName2.call(obj) //window</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<p>上面代码中都是定义在全局作用域中，而箭头函数<code>getName2()</code> this始终指向全局作用域，不随 <code>call()</code> 的调用而改变</p>
<p>如在回调函数中使用
let div = document.getElementById(“test-div”)
div.onclick = function () {
// setTimeout(function () {
//     console.log(this) // 此时this是window
//     this.style.background = “red”
// },2000)</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>     </span></span><span>setTimeout(() =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>// 此时this是外部作用域div</span></div></div><div><div><div>3</div></div><div><span><span>         </span></span><span>this.style.background = "red"</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>}, 2000)</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>不能作为构造实例化对象,也就是说，不可以使用new命令，否则会抛出一个错误</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let Person = (name, age) =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>this.name = name</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>this.age = age</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>let me = new Person('tom',30)</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>//Uncaught TypeError: Person is not a constructor</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>console.log(me)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>不能使用 <code>arguments</code> 变量,该对象在函数体内不存在。如果要用，可以用 <code>rest</code> 参数代替</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let fun = () =&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>console.log(arguments);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>fun(1, 2)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>// ReferenceError: arguments is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/bian-liang-jie-gou-fu-zhi/</id>
      <title type="text">变量解构赋值</title>
      <published>2021-11-06T22:57:08.000Z</published>
      <updated>2021-11-06T22:57:08.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/bian-liang-jie-gou-fu-zhi/"/>
      <summary type="text">ES6 允许按照一定模式从数组和对象中提取值，对变量进行赋值，这被称为解构赋值。</summary>
      <content type="html"><![CDATA[<section><h1>变量的解构赋值<a href="#变量的解构赋值"><span>#</span></a></h1><p>ES6 允许按照一定模式从数组和对象中提取值，对变量进行赋值，这被称为解构赋值。</p><section><h2>数组的解构赋值<a href="#数组的解构赋值"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const ARR = ["1", "2", "3"]</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let [a, b, c] = ARR</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>//等价于 let [a, b, c] = ["1", "2", "3"]</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>console.log(a, b, c) // 1 2 3</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码表示，可以从数组中提取值，按照对应位置，对变量赋值。</p><p>本质上，这种写法属于“模式匹配”，只要等号两边的模式相同，左边的变量就会被赋予对应的值。</p><p>下面是一些使用嵌套数组进行解构的例子</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let [, , third] = ["foo", "bar", "baz"]</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(third) // "baz"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>let [x, , y] = [1, 2, 3]</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log(x) // 1</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console.log(y) // 3</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>// ...x 为匹配剩余项</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>let [head, ...tail] = [1, 2, 3, 4]</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>console.log(head) // 1</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console.log(tail) // [2, 3, 4]</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>let [x, y, ...z] = ["a"]</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>console.log(x) // "a"</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>console.log(y) // undefined</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>console.log(z) // []</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果解构不成功，变量的值就等于undefined。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let [foo] = []</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let [bar, foo] = [1]</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>// foo的值都为undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>解构赋值允许指定默认值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let [foo = true] = []</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(foo) // true</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>let [x, y = "b"] = ["a"]; // x='a', y='b'</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>let [x, y = "b"] = ["a", undefined]; // x='a', y='b'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，ES6 内部使用严格相等运算符(===)，判断一个位置是否有值。所以，只有当一个数组成员严格等于undefined，默认值才会生效。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let [x = 1] = [undefined]</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(x) // 1</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>let [y = 1] = [null];</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console.log(y) // null</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>对象的解构赋值<a href="#对象的解构赋值"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>const obj = {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>name: "tom",</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>age: 18,</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>fun: function () {</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>console.log("i am tom")</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>let {name, age, fun} = obj</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console.log(name, age, fun) // tom 18 function()</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>fun() // i am tom</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对象的解构与数组有一个重要的不同。数组的元素是按次序排列的，变量的取值由它的位置决定；而对象的属性没有次序，变量必须与属性同名，才能取到正确的值，否则为undefined</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let {age, sex} = obj</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(age, sex) //18 undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果变量名与属性名不一致，必须写成下面这样:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let { foo: baz } = { foo: "aaa", bar: "bbb" };</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(baz) // aaa</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>console.log(foo) //Uncaught ReferenceError: foo is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也就是说，对象的解构赋值的内部机制，是先找到同名属性，然后再赋给对应的变量，即 <code>foo</code> 是匹配的模式， <code>baz</code> 才是变量。真正被赋值的是变量 <code>baz</code> ，而不是模式 <code>foo</code></p><p>对象的解构也可以指定默认值,默认值生效的条件是，对象的属性值严格等于<code>undefined</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let { x = 3 } = {}</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(x) // 3</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>let { x, y = 5 } = { x: 1 }</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console.log(x) // 1</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>console.log(y) // 5</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>let { x: y = 3 } = { x: 5 }</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>console.log(y) // 5</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果解构失败，变量的值等于undefined。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let { foo } = { bar: "baz" }</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(foo) // undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>字符串的解构赋值<a href="#字符串的解构赋值"><span>#</span></a></h2><p>字符串也可以解构赋值。这是因为此时，字符串被转换成了一个类似数组的对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let [a, b, c, d, e] = "hello"</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(a, b, c, d, e) // h e l l o</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>类似数组的对象都有一个length属性，因此还可以对这个属性解构赋值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>let {length} = "hello</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(length) //5</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>数值和布尔值的解构赋值<a href="#数值和布尔值的解构赋值"><span>#</span></a></h2><p>解构赋值时，如果等号右边是数值和布尔值，则会先转为对象。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let {toString: s} = 123</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>s === Number.prototype.toString // true</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>let {toString: s} = true</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>s === Boolean.prototype.toString // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，数值和布尔值的包装对象都有toString属性，因此变量s都能取到值。</p><p>解构赋值的规则是，只要等号右边的值不是对象或数组，就先将其转为对象。由于undefined和null无法转为对象，所以对它们进行解构赋值，都会报错。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>let { prop: x } = undefined // TypeError</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>let { prop: y } = null // TypeError</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>函数参数的解构赋值<a href="#函数参数的解构赋值"><span>#</span></a></h2><p>函数的参数也可以使用解构赋值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function add([x, y]){</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>return x + y</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>add([1, 2]) // 3</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>[[1, 2], [3, 4]].map(([a, b]) =&gt; a + b)  // [ 3, 7 ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码中，函数<code>add()</code>的参数表面上是一个数组，但在传入参数的那一刻，数组参数就被解构成变量<code>x</code>和<code>y</code>。对于函数内部的代码来说，它们能感受到的参数就是<code>x</code>和<code>y</code></p><p>函数参数的解构也可以使用默认值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function move({x = 0, y = 0} = {}) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>return [x, y]</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>move({x: 3, y: 8});// [3, 8]</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>move({x: 3}) // [3, 0]</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>move({}) // [0, 0]</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>move() // [0, 0]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>下面代码是为函数move的参数指定默认值，而不是为变量x和y指定默认值，所以会得到与前一种写法不同的结果<code>undefined</code>就会触发函数参数的默认值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function move({x, y} = { x: 0, y: 0 }) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>return [x, y]</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>move({x: 3, y: 8}) // [3, 8]</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>move({x: 3}) // [3, undefined]</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>move({}) // [undefined, undefined]</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>move() // [0, 0]</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>[1, undefined, 3].map((x = 'yes') =&gt; x)// [ 1, 'yes', 3 ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/let-yu-const/</id>
      <title type="text">let与const</title>
      <published>2021-10-31T03:02:40.000Z</published>
      <updated>2021-10-31T03:02:40.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/let-yu-const/"/>
      <summary type="text">在ES6中引入了变量声明的两种方式，let 与 conset let 用来声明变量 conset 用来声明常量</summary>
      <content type="html"><![CDATA[<section><h1>let与conset<a href="#let与conset"><span>#</span></a></h1><p>在ES6中引入了变量声明的两种方式，let 与 conset</p><ul>
<li>let 用来声明变量</li>
<li>conset 用来声明常量</li>
</ul><section><h2>let命令<a href="#let命令"><span>#</span></a></h2><section><h3>基本用法<a href="#基本用法"><span>#</span></a></h3><p>ES6 新增了let命令，用来声明变量。它的用法类似于var，但是所声明的变量，只在let命令所在的代码块内有效。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>let a = 10</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>var b = 1</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>a // ReferenceError: a is not defined.</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>b // 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面代码在代码块之中，分别用let和var声明了两个变量。然后在代码块之外调用这两个变量，结果let声明的变量报错，var声明的变量返回了正确的值。这表明，let声明的变量只在它所在的代码块有效。</p><p>例：通过定时器每秒打印0-4</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>for(var i = 0; i &lt; 5; i++){</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>setTimeout(function(){</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>alert(i)</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},1000)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// 5 5 5 5 5</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因为ES5之前没有块级作用域的概念，所以循环定时器、回调函数执行时的 i 定义在<code>window</code>中，当回调函数执行时，会向上级作用域中寻找到<code>window.i </code>此时 <code>i === 5</code>，故</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>for(let i = 0; i &lt; 5; i++){</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>setTimeout(function(){</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>alert(i)</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>},1000)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// 0 1 2 3 4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在块级作用域中，使用<code>let</code>定义 i , 相当于在每个块级作用域中定义了一个i，回调函数不必在全局作用域中寻找i的值</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>        </span></span><span>for (let i = 0; i &lt; 5; i++) {</span></div></div><div><div><div>2</div></div><div><span><span>            </span></span><span>setTimeout(function () {</span></div></div><div><div><div>3</div></div><div><span><span>                </span></span><span>alert(i)</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>}, 1000)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>let i = 0</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>setTimeout(function () {</span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>alert(i)</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>}, 1000)</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>let i = 1</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>setTimeout(function () {</span></div></div><div><div><div>15</div></div><div><span><span>                </span></span><span>alert(i)</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>}, 1000)</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>不存在变量提升<a href="#不存在变量提升"><span>#</span></a></h3><p>var命令会发生”变量提升“现象，即变量可以在声明之前使用，值为undefined。
而let命令改变了语法行为，它所声明的变量一定要在声明后使用，否则报错。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// var 的情况</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console.log(foo) // 输出undefined</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>var foo = 2</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// let 的情况</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console.log(bar) // 报错ReferenceError</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>let bar = 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>不允许重复声明<a href="#不允许重复声明"><span>#</span></a></h3><p><code>let</code> 不允许在相同作用域内，重复声明同一个变量。<code>var</code>可以</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>function fun1() {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>let a = 10</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>var a = 1</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// 报错</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>function fuc2() {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>let a = 10</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>let a = 1</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>// 正常</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>function fu() {</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>var a = 10</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>var a = 1</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>conset命令<a href="#conset命令"><span>#</span></a></h2><section><h3>基本用法<a href="#基本用法-1"><span>#</span></a></h3><p><code>const</code> 声明一个只读的常量。一旦声明，常量的值就不能改变，一般用 <code>conset</code> 声明的常量用大写表示</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const PI = 3.1415</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>PI // 3.1415</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>PI = 3</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>// TypeError: Assignment to constant variable.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>const声明的变量不得改变值，这意味着，const一旦声明变量，就必须立即初始化，不能留到以后赋值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const ITEM</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// SyntaxError: Missing initializer in const declaration</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>const的作用域与let命令相同: 只在声明所在的块级作用域内有效。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>if (true) {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>const MAX = 5</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>console.log(MAX) //ReferenceError: MAX is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>conset的本质<a href="#conset的本质"><span>#</span></a></h3><ul>
<li>
<p>const实际上保证的，并不是变量的值不得改动，而是变量指向的那个内存地址所保存的数据不得改动</p>
</li>
<li>
<p>对于<strong>简单类型的数据</strong>(数值、字符串、布尔值)，值就保存在变量指向的那个内存地址，因此等同于常量</p>
</li>
<li>
<p>对于<strong>复合类型的数据</strong>(主要是对象和数组)，变量指向的内存地址，保存的只是一个指向实际数据的指针，const只能保证这个指针是固定的(即总是指向另一个固定的地址)，至于它指向的数据结构是不是可变的，就完全不能控制了</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const ARR = ["1", "2", "3", "4"]</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>ARR.push("5")</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>console.log(ARR) //['1', '2', '3', '4', '5']</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>ARR = ["a", "b", "c"] //TypeError: Assignment to constant variable.</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>对于数组和对象的元素修改, 不算做对常量的修改, 不会报错，因为conset 只能<strong>确保对象所指向的地址不能改变</strong>，而地址的内容就无法保证了</p></section></section><section><h2>块级作用域<a href="#块级作用域"><span>#</span></a></h2><section><h3>为什么需要块级作用域<a href="#为什么需要块级作用域"><span>#</span></a></h3><p>ES5 只有全局作用域和函数作用域，没有块级作用域，这带来很多不合理的场景。</p><ul>
<li>
<p>第一种场景，内层变量可能会覆盖外层变量。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var tmp = new Date()</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>function fun() {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>console.log(tmp)</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>if (false) {</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>var tmp = "hello world"</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>fun(); // undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码的原意是，if代码块的外部使用外层的 <code>tmp</code> 变量，内部使用内层的 <code>tmp</code> 变量。但是，函数<code>fun()</code>执行后，输出结果为 <code>undefined</code>，原因在于变量提升，导致内层的 <code>tmp</code> 变量覆盖了外层的 <code>tmp</code> 变量。</p>
</li>
<li>
<p>第二种场景，用来计数的循环变量泄露为全局变量。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>for(var i = 0; i &lt; 5; i++){</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>setTimeout(function(){</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>alert(i)</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>},1000)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>// 5 5 5 5 5</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码中i定义为全局变量，导致回调函数执行时，使用的是全局作用域中的 i</p>
</li>
</ul></section><section><h3>ES6 的块级作用域<a href="#es6-的块级作用域"><span>#</span></a></h3><p>用let命令新增了块级作用域，外层作用域无法获取到内层作用域，内层作用域能获取到外层作用域声明的变量</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>   </span></span><span>{</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>let a = "aa"</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>console.log(a) // aa</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>console.log(b)  // ReferrenceError</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>let b = "bb"</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>同时内层作用域可以定义外层作用域的同名变量</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>let a = "Hello World"</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>let a = "Hello World"</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>块级作用域与函数声明<a href="#块级作用域与函数声明"><span>#</span></a></h3><p>运行环境：ES6浏览器。</p><p>可实现的行为：</p><ol>
<li>
<p>允许在块级作用域内声明函数</p>
</li>
<li>
<p>函数声明类似于var， 即会提升到全局作用域或函数作用域的头部</p>
</li>
<li>
<p>函数声明还会提升到所有块级作用域的头部</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>function func() {</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>console.log('outside')</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>(function () {</span></div></div><div><div><div>5</div></div><div><span><span>     </span></span><span>// var Func = undefined;</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>if (false) {</span></div></div><div><div><div>7</div></div><div><span><span>         </span></span><span>function func() {</span></div></div><div><div><div>8</div></div><div><span><span>             </span></span><span>console.log('inside')</span></div></div><div><div><div>9</div></div><div><span><span>         </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>     </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>     </span></span><span>func() //Uncaught TypeError: func is not a function</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>})() //报错</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol><p>考虑到环境导致的差异太大，应该避免在块级作用域中声明函数，如果需要，应该写成函数表达式的形式，而不是函数声明语句</p><p>ES6的块级作用域允许声明函数的规则只在使用大括号的情况下成立：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>'use strict'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if (true) {</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>function Func1() {} // 不报错</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>if (true)</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>function Func2() {} // 报错</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>// In strict mode code, functions can only be declared</span></div></div><div><div><div>8</div></div><div><span><span>       </span></span><span>at top level or inside a block.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/es6-jian-jie/</id>
      <title type="text">ES6简介</title>
      <published>2021-10-31T03:00:28.000Z</published>
      <updated>2021-10-31T03:00:28.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/es6-jian-jie/"/>
      <summary type="text">ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准，已经在 2015 年 6 月正式发布了。它的目标，是使得 JavaScript 语言可以用来编写复杂的大型应用程序，成为企业级开发语言。</summary>
      <content type="html"><![CDATA[<p>ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准，已经在 2015 年 6 月正式发布了。它的目标，是使得 JavaScript 语言可以用来编写复杂的大型应用程序，成为企业级开发语言。</p>
<section><h2>ECMAScript 和 JavaScript 的关系<a href="#ecmascript-和-javascript-的关系"><span>#</span></a></h2><p>一个常见的问题是，ECMAScript 和 JavaScript 到底是什么关系？
要讲清楚这个问题，需要回顾历史。1996 年 11 月，JavaScript 的创造者 Netscape 公司，决定将 JavaScript 提交给标准化组织 ECMA，希望这种语言能够成为国际标准。次年，ECMA 发布 262 号标准文件(ECMA-262)的第一版，规定了浏览器脚本语言的标准，并将这种语言称为 ECMAScript，这个版本就是 1.0 版。</p><p>该标准从一开始就是针对 JavaScript 语言制定的，但是之所以不叫 JavaScript，有两个原因。一是商标，Java 是 Sun 公司的商标，根据授权协议，只有 Netscape 公司可以合法地使用 JavaScript 这个名字，且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA，不是 Netscape，这样有利于保证这门语言的开放性和中立性。</p><p>因此，ECMAScript 和 JavaScript 的关系是，前者是后者的规格，后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。日常场合，这两个词是可以互换的。</p></section>
<section><h2>ES6 与 ECMAScript 2015 的关系<a href="#es6-与-ecmascript-2015-的关系"><span>#</span></a></h2><p>ECMAScript 2015(简称 ES2015)这个词，也是经常可以看到的。它与 ES6 是什么关系呢？</p><p>2011 年，ECMAScript 5.1 版发布后，就开始制定 6.0 版了。因此，ES6 这个词的原意，就是指 JavaScript 语言的下一个版本。</p><p>但是，因为这个版本引入的语法功能太多，而且制定过程当中，还有很多组织和个人不断提交新功能。事情很快就变得清楚了，不可能在一个版本里面包括所有将要引入的功能。常规的做法是先发布 6.0 版，过一段时间再发 6.1 版，然后是 6.2 版、6.3 版等等。
但是，标准的制定者不想这样做。他们想让标准的升级成为常规流程: 任何人在任何时候，都可以向标准委员会提交新语法的提案，然后标准委员会每个月开一次会，评估这些提案是否可以接受，需要哪些改进。如果经过多次会议以后，一个提案足够成熟了，就可以正式进入标准了。这就是说，标准的版本升级成为了一个不断滚动的流程，每个月都会有变动。</p><p>标准委员会最终决定，标准在每年的 6 月份正式发布一次，作为当年的正式版本。接下来的时间，就在这个版本的基础上做改动，直到下一年的 6 月份，草案就自然变成了新一年的版本。这样一来，就不需要以前的版本号了，只要用年份标记就可以了。</p><p>ES6 的第一个版本，就这样在 2015 年 6 月发布了，正式名称就是《ECMAScript 2015 标准》(简称 ES2015)。2016 年 6 月，小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布，这个版本可以看作是 ES6.1 版，因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符)，基本上是同一个标准。根据计划，2017 年 6 月发布 ES2017 标准。
因此，ES6 既是一个历史名词，也是一个泛指，含义是 5.1 版以后的 JavaScript 的下一代标准，涵盖了 ES2015、ES2016、ES2017 等等，而 ES2015 则是正式名称，特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方，一般是指 ES2015 标准，但有时也是泛指“下一代 JavaScript 语言”。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-lun-bo-tu/</id>
      <title type="text">jQuery轮播图</title>
      <published>2021-10-27T07:13:14.000Z</published>
      <updated>2021-10-27T07:13:14.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-lun-bo-tu/"/>
      <summary type="text">功能说明: 1. 点击向右(左)的图标, 平滑切换到下(上)一页 2. 无限循环切换: 第一页的上一页为最后页, 最后一页的下一页是第一页 3. 每隔3s自动滑动到下一页 4. 当鼠标进入图片区域时, 自动切换停止, 当鼠标离开后,又开始自动切换 5. 切换页面时, 下面的圆点也同步更新 6. 点击…</summary>
      <content type="html"><![CDATA[<section><h1>jQuery实现轮播图<a href="#jquery实现轮播图"><span>#</span></a></h1><p>功能说明:</p><ol>
<li>点击向右(左)的图标, 平滑切换到下(上)一页</li>
<li>无限循环切换: 第一页的上一页为最后页, 最后一页的下一页是第一页</li>
<li>每隔3s自动滑动到下一页</li>
<li>当鼠标进入图片区域时, 自动切换停止, 当鼠标离开后,又开始自动切换</li>
<li>切换页面时, 下面的圆点也同步更新</li>
<li>点击圆点图标切换到对应的页</li>
</ol><p>bug: 快速点击时, 翻页不正常</p><section><h2>代码演示<a href="#代码演示"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// html 部分</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;div id="container"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;div id="list" style="left: -600px;"&gt;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>&lt;img src="img/5.jpg" alt="5" /&gt;</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>&lt;img src="img/1.jpg" alt="1" /&gt;</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>&lt;img src="img/2.jpg" alt="2" /&gt;</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>&lt;img src="img/3.jpg" alt="3" /&gt;</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>&lt;img src="img/4.jpg" alt="4" /&gt;</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>&lt;img src="img/5.jpg" alt="5" /&gt;</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>&lt;img src="img/1.jpg" alt="1" /&gt;</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>&lt;div id="pointsDiv"&gt;</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>&lt;span index="1" class="on"&gt;&lt;/span&gt;</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>&lt;span index="2"&gt;&lt;/span&gt;</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>&lt;span index="3"&gt;&lt;/span&gt;</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>&lt;span index="4"&gt;&lt;/span&gt;</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>&lt;span index="5"&gt;&lt;/span&gt;</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>&lt;a href="javascript:;" id="prev" class="arrow"&gt;&amp;lt;&lt;/a&gt;</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>&lt;a href="javascript:;" id="next" class="arrow"&gt;&amp;gt;&lt;/a&gt;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>// js 部分</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>$(function () {</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>var $container = $('#container')    //获取整个轮播图外部div</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>var $list = $('#list')  //获取图片列表div</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>var $points = $('#pointsDiv&gt;span')  // 获取圆点sapn</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>var $prev = $('#prev')  // 获取上一张按钮</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>var $next = $('#next')  // 获取下一张按钮</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>var PAGE_WIDTH = 600 //一页的宽度</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>var TIME = 400 // 翻页的持续时间</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>var ITEM_TIME = 20 // 单元移动的间隔时间</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>var imgCount = $points.length   //img张数</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>var index = 0 //当前下标</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>var moving = false // 标识是否正在翻页(默认没有)</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>// 1. 点击向右(左)的图标, 平滑切换到下(上)一页</span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>$next.click(function () {</span></div></div><div><div><div>41</div></div><div><span><span>            </span></span><span>// 平滑翻到下一页</span></div></div><div><div><div>42</div></div><div><span><span>            </span></span><span>nextPage(true)</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>$prev.click(function () {</span></div></div><div><div><div>45</div></div><div><span><span>            </span></span><span>// 平滑翻到上一页</span></div></div><div><div><div>46</div></div><div><span><span>            </span></span><span>nextPage(false)</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>// 3. 每隔3s自动滑动到下一页</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>var intervalId = setInterval(function () {</span></div></div><div><div><div>51</div></div><div><span><span>            </span></span><span>nextPage(true)</span></div></div><div><div><div>52</div></div><div><span><span>        </span></span><span>}, 1000)</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span><span>        </span></span><span>// 4. 当鼠标进入图片区域时, 自动切换停止, 当鼠标离开后,又开始自动切换</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>$container.hover(function () {</span></div></div><div><div><div>56</div></div><div><span><span>            </span></span><span>// 清除定时器</span></div></div><div><div><div>57</div></div><div><span><span>            </span></span><span>clearInterval(intervalId)</span></div></div><div><div><div>58</div></div><div><span><span>        </span></span><span>}, function () {</span></div></div><div><div><div>59</div></div><div><span><span>            </span></span><span>intervalId = setInterval(function () {</span></div></div><div><div><div>60</div></div><div><span><span>                </span></span><span>nextPage(true)</span></div></div><div><div><div>61</div></div><div><span><span>            </span></span><span>}, 1000)</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span><span>        </span></span><span>// 6. 点击圆点图标切换到对应的页</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>$points.click(function () {</span></div></div><div><div><div>66</div></div><div><span><span>            </span></span><span>// 目标页的下标</span></div></div><div><div><div>67</div></div><div><span><span>            </span></span><span>var targetIndex = $(this).index()</span></div></div><div><div><div>68</div></div><div><span><span>            </span></span><span>// 只有当点击的不是当前页的圆点时才翻页</span></div></div><div><div><div>69</div></div><div><span><span>            </span></span><span>if (targetIndex != index) {</span></div></div><div><div><div>70</div></div><div><span><span>                </span></span><span>nextPage(targetIndex)</span></div></div><div><div><div>71</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>72</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span><span>        </span></span><span>/**</span></div></div><div><div><div>75</div></div><div><span><span>         </span></span><span>* 平滑翻页</span></div></div><div><div><div>76</div></div><div><span><span>         </span></span><span>* @param next</span></div></div><div><div><div>77</div></div><div><span><span>         </span></span><span>* true: 下一页</span></div></div><div><div><div>78</div></div><div><span><span>         </span></span><span>* false: 上一页</span></div></div><div><div><div>79</div></div><div><span><span>         </span></span><span>* 数值: 指定下标页</span></div></div><div><div><div>80</div></div><div><span><span>         </span></span><span>*/</span></div></div><div><div><div>81</div></div><div><span><span>        </span></span><span>function nextPage(next) {</span></div></div><div><div><div>82</div></div><div><span><span>            </span></span><span>/*</span></div></div><div><div><div>83</div></div><div><span><span>              </span></span><span>总的时间: TIME=400</span></div></div><div><div><div>84</div></div><div><span><span>              </span></span><span>单元移动的间隔时间: ITEM_TIME = 20</span></div></div><div><div><div>85</div></div><div><span><span>              </span></span><span>总的偏移量: offset</span></div></div><div><div><div>86</div></div><div><span><span>              </span></span><span>单元移动的偏移量: itemOffset = offset/(TIME/ITEM_TIME)</span></div></div><div><div><div>87</div></div><div>
</div></div><div><div><div>88</div></div><div><span><span>              </span></span><span>启动循环定时器不断更新$list的left, 到达目标处停止停止定时器</span></div></div><div><div><div>89</div></div><div><span><span>             </span></span><span>*/</span></div></div><div><div><div>90</div></div><div>
</div></div><div><div><div>91</div></div><div><span><span>            </span></span><span>//如果正在翻页, 直接结束</span></div></div><div><div><div>92</div></div><div><span><span>            </span></span><span>if (moving) { //已经正在翻页中</span></div></div><div><div><div>93</div></div><div><span><span>                </span></span><span>return</span></div></div><div><div><div>94</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>95</div></div><div><span><span>            </span></span><span>moving = true // 标识正在翻页</span></div></div><div><div><div>96</div></div><div>
</div></div><div><div><div>97</div></div><div><span><span>            </span></span><span>// 总的偏移量: offset</span></div></div><div><div><div>98</div></div><div><span><span>            </span></span><span>var offset = 0</span></div></div><div><div><div>99</div></div><div><span><span>            </span></span><span>// 计算offset</span></div></div><div><div><div>100</div></div><div><span><span>            </span></span><span>if (typeof next === 'boolean') {</span></div></div><div><div><div>101</div></div><div><span><span>                </span></span><span>offset = next ? -PAGE_WIDTH : PAGE_WIDTH</span></div></div><div><div><div>102</div></div><div><span><span>            </span></span><span>} else {</span></div></div><div><div><div>103</div></div><div><span><span>                </span></span><span>offset = -(next - index) * PAGE_WIDTH</span></div></div><div><div><div>104</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>105</div></div><div>
</div></div><div><div><div>106</div></div><div><span><span>            </span></span><span>// 计算单元移动的偏移量: itemOffset</span></div></div><div><div><div>107</div></div><div><span><span>            </span></span><span>var itemOffset = offset / (TIME / ITEM_TIME)</span></div></div><div><div><div>108</div></div><div><span><span>            </span></span><span>// 得到当前的left值</span></div></div><div><div><div>109</div></div><div><span><span>            </span></span><span>var currLeft = $list.position().left</span></div></div><div><div><div>110</div></div><div><span><span>            </span></span><span>// 计算出目标处的left值</span></div></div><div><div><div>111</div></div><div><span><span>            </span></span><span>var targetLeft = currLeft + offset</span></div></div><div><div><div>112</div></div><div>
</div></div><div><div><div>113</div></div><div><span><span>            </span></span><span>// 启动循环定时器不断更新$list的left, 到达目标处停止停止定时器</span></div></div><div><div><div>114</div></div><div><span><span>            </span></span><span>var intervalId = setInterval(function () {</span></div></div><div><div><div>115</div></div><div><span><span>                </span></span><span>// 计算出最新的currLeft</span></div></div><div><div><div>116</div></div><div><span><span>                </span></span><span>currLeft += itemOffset</span></div></div><div><div><div>117</div></div><div><span><span>                </span></span><span>if (currLeft === targetLeft) { // 到达目标位置</span></div></div><div><div><div>118</div></div><div><span><span>                    </span></span><span>// 清除定时器</span></div></div><div><div><div>119</div></div><div><span><span>                    </span></span><span>clearInterval(intervalId)</span></div></div><div><div><div>120</div></div><div><span><span>                    </span></span><span>// 标识翻页停止</span></div></div><div><div><div>121</div></div><div><span><span>                    </span></span><span>moving = false</span></div></div><div><div><div>122</div></div><div><span><span>                    </span></span><span>// 如果到达了最右边的图片(1.jpg), 跳转到最左边的第2张图片(1.jpg)</span></div></div><div><div><div>123</div></div><div><span><span>                    </span></span><span>if (currLeft === -(imgCount + 1) * PAGE_WIDTH) {</span></div></div><div><div><div>124</div></div><div><span><span>                        </span></span><span>currLeft = -PAGE_WIDTH</span></div></div><div><div><div>125</div></div><div><span><span>                    </span></span><span>} else if (currLeft === 0) {</span></div></div><div><div><div>126</div></div><div><span><span>                        </span></span><span>// 如果到达了最左边的图片(5.jpg), 跳转到最右边的第2张图片(5.jpg)</span></div></div><div><div><div>127</div></div><div><span><span>                        </span></span><span>currLeft = -imgCount * PAGE_WIDTH</span></div></div><div><div><div>128</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>129</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>130</div></div><div><span><span>                </span></span><span>// 设置left</span></div></div><div><div><div>131</div></div><div><span><span>                </span></span><span>$list.css('left', currLeft)</span></div></div><div><div><div>132</div></div><div><span><span>                </span></span><span>// console.log(currLeft)</span></div></div><div><div><div>133</div></div><div><span><span>            </span></span><span>}, ITEM_TIME)</span></div></div><div><div><div>134</div></div><div>
</div></div><div><div><div>135</div></div><div><span><span>            </span></span><span>// 更新圆点</span></div></div><div><div><div>136</div></div><div><span><span>            </span></span><span>updatePoints(next)</span></div></div><div><div><div>137</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>138</div></div><div>
</div></div><div><div><div>139</div></div><div><span><span>        </span></span><span>/**</span></div></div><div><div><div>140</div></div><div><span><span>         </span></span><span>* 更新圆点</span></div></div><div><div><div>141</div></div><div><span><span>         </span></span><span>* @param next</span></div></div><div><div><div>142</div></div><div><span><span>         </span></span><span>*/</span></div></div><div><div><div>143</div></div><div><span><span>        </span></span><span>function updatePoints(next) {</span></div></div><div><div><div>144</div></div><div><span><span>            </span></span><span>// 计算出目标圆点的下标targetIndex</span></div></div><div><div><div>145</div></div><div><span><span>            </span></span><span>var targetIndex = 0</span></div></div><div><div><div>146</div></div><div><span><span>            </span></span><span>if (typeof next === 'boolean') {</span></div></div><div><div><div>147</div></div><div><span><span>                </span></span><span>if (next) {</span></div></div><div><div><div>148</div></div><div><span><span>                    </span></span><span>targetIndex = index + 1 // [0, imgCount-1]</span></div></div><div><div><div>149</div></div><div><span><span>                    </span></span><span>if (targetIndex === imgCount) { // 此时看到的是1.jpg--&gt;第1个圆点</span></div></div><div><div><div>150</div></div><div><span><span>                        </span></span><span>targetIndex = 0</span></div></div><div><div><div>151</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>152</div></div><div><span><span>                </span></span><span>} else {</span></div></div><div><div><div>153</div></div><div><span><span>                    </span></span><span>targetIndex = index - 1</span></div></div><div><div><div>154</div></div><div><span><span>                    </span></span><span>if (targetIndex === -1) { // 此时看到的是5.jpg--&gt;第5个圆点</span></div></div><div><div><div>155</div></div><div><span><span>                        </span></span><span>targetIndex = imgCount - 1</span></div></div><div><div><div>156</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>157</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>158</div></div><div><span><span>            </span></span><span>} else {</span></div></div><div><div><div>159</div></div><div><span><span>                </span></span><span>targetIndex = next</span></div></div><div><div><div>160</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>161</div></div><div>
</div></div><div><div><div>162</div></div><div>
</div></div><div><div><div>163</div></div><div><span><span>            </span></span><span>// 将当前index的&lt;span&gt;的class移除</span></div></div><div><div><div>164</div></div><div><span><span>            </span></span><span>// $points.eq(index).removeClass('on')</span></div></div><div><div><div>165</div></div><div><span><span>            </span></span><span>$points[index].className = ''</span></div></div><div><div><div>166</div></div><div><span><span>            </span></span><span>// 给目标圆点添加class='on'</span></div></div><div><div><div>167</div></div><div><span><span>            </span></span><span>// $points.eq(targetIndex).addClass('on')</span></div></div><div><div><div>168</div></div><div><span><span>            </span></span><span>$points[targetIndex].className = 'on'</span></div></div><div><div><div>169</div></div><div>
</div></div><div><div><div>170</div></div><div><span><span>            </span></span><span>// 将index更新为targetIndex</span></div></div><div><div><div>171</div></div><div><span><span>            </span></span><span>index = targetIndex</span></div></div><div><div><div>172</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>173</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>知识要点<a href="#知识要点"><span>#</span></a></h2><ul>
<li>
<p>上段代码中图片切换通过<code>nextPage(next)</code> 函数实现，这样的好处是方便调用，如点击切换，以及循环定时器自调用中。同时圆点的更新和图片切换同时执行。</p>
</li>
<li>
<p><code>nextPage(next)</code> 函数</p>
<ol>
<li>
<p>整体思路
轮播图实际上是图片的分段移动，通过不断调用循环定时器让图片分段移动，故总偏移量为一张或多张图片宽度，每段时间间隔移动一点距离，到达指定位置后就清除定时器</p>
</li>
<li>
<p>next 参数的设置
next 参数有三个值，ture：下一页 ，false ：上一页 ，数值：指定下标页</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>if (typeof next === 'boolean') {</span></div></div><div><div><div>2</div></div><div><span><span>   </span></span><span>//点击上下页按钮时触发</span></div></div><div><div><div>3</div></div><div><span><span>   </span></span><span>offset = next ? -PAGE_WIDTH : PAGE_WIDTH</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>} else {</span></div></div><div><div><div>5</div></div><div><span><span>   </span></span><span>// 点击圆点跳转指定页触发</span></div></div><div><div><div>6</div></div><div><span><span>   </span></span><span>offset = -(next - index) * PAGE_WIDTH</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如何解决图片循环展示问题</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>// 如果到达了最右边的图片(1.jpg), 跳转到最左边的第2张图片(1.jpg)</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>if (currLeft === -(imgCount + 1) * PAGE_WIDTH) {</span></div></div><div><div><div>3</div></div><div><span><span>   </span></span><span>currLeft = -PAGE_WIDTH</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>} else if (currLeft === 0) {</span></div></div><div><div><div>5</div></div><div><span><span>   </span></span><span>// 如果到达了最左边的图片(5.jpg), 跳转到最右边的第2张图片(5.jpg)</span></div></div><div><div><div>6</div></div><div><span><span>   </span></span><span>currLeft = -imgCount * PAGE_WIDTH</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>如下图所示，当图片的<code>left === -600*5</code> 时，此时为循环展示的最后一张图片，此时将图片切换到列表第一张图片，以达到无缝切换，第一张同理</p>
<p></p><figure><img src="/images/jQuery/jQuery%E8%BD%AE%E6%92%AD%E5%9B%BE1.png" alt="jQuery轮播图1.png" /><figcaption>jQuery轮播图1.png</figcaption></figure><p></p>
</li>
<li>
<p>如何解决快速点击时, 翻页不正常
产生原因：同时执行了多个循环定时器，导致图片div的left属性设置不正常
解决办法：通过设置标识符判断循环定时器是否在执行，如果在执行则停止当前正在执行的定时器，开启新的定时器
注：在执行<code>nextPage(next)</code> 时 <code>moving</code> 设置为 <code>true</code>，清除定时器时 <code>moving</code> 设置为 <code>false</code></p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>var moving = false //默认没有反页</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>nextPage(next){</span></div></div><div><div><div>3</div></div><div><span><span>   </span></span><span>if (moving) { //如果正在翻页中</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>return</span></div></div><div><div><div>5</div></div><div><span><span>   </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>   </span></span><span>moving = true // 标识正在翻页</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>   </span></span><span>// 设置定时器</span></div></div><div><div><div>9</div></div><div><span><span>   </span></span><span>var intervalId = setInterval(function () {</span></div></div><div><div><div>10</div></div><div><span><span>       </span></span><span>// 。。。</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>   </span></span><span>// 到达目标位置</span></div></div><div><div><div>13</div></div><div><span><span>   </span></span><span>if (currLeft === targetLeft) {</span></div></div><div><div><div>14</div></div><div><span><span>   </span></span><span>// 清除定时器</span></div></div><div><div><div>15</div></div><div><span><span>   </span></span><span>clearInterval(intervalId)</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>   </span></span><span>// 标识翻页停止</span></div></div><div><div><div>18</div></div><div><span><span>   </span></span><span>moving = false</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span><span>       </span></span><span>// 。。。</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>     </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>   </span></span><span>}, ITEM_TIME)</span></div></div><div><div><div>24</div></div><div><span><span> </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ol>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-cha-jian/</id>
      <title type="text">jQuery插件</title>
      <published>2021-10-25T05:21:52.000Z</published>
      <updated>2021-10-25T05:21:52.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-cha-jian/"/>
      <summary type="text">我们可以扩展 jQuery 来实现自定义方法。这种方式也称为编写 jQuery 插件。 在使用jQuery插件时，应该先引入jQuery库文件，然后在引用jQuery插件</summary>
      <content type="html"><![CDATA[<ul>
<li>我们可以扩展 jQuery 来实现自定义方法。这种方式也称为编写 jQuery 插件。</li>
<li>在使用jQuery插件时，应该先引入jQuery库文件，然后在引用jQuery插件</li>
</ul>
<section><h2>扩展jQuery的工具方法<a href="#扩展jquery的工具方法"><span>#</span></a></h2><p>扩展jQuery的工具方法，<code>$.extend(object)</code>
用来在jQuery命名空间上增加新函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>//min方法返回一个较小值，max返回一个较大值</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>$.extend({</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>min: function (a, b) {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>return a &lt; b ? a : b</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>max: function (a, b) {</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>return a &gt; b ? a : b</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>})</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>//使用</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>$.min(3, 5) //3</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>$.max(3, 5) //5</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>扩展jQuery对象的方法<a href="#扩展jquery对象的方法"><span>#</span></a></h2><p>扩展jQuery对象的方法，<code>$.fn.extend(object)</code>
扩展 jQuery 对象来提供新的方法</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$.fn.extend({</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>// checkAll() : 全选</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>checkAll: function () {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>this.prop('checked', true) // this是jQuery对象</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>return this   // this是jQuery对象</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>// unCheckAll() : 全不选</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>unCheckAll: function () {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>this.prop('checked', false)</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>return this</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>// reverseCheck() : 全反选</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>reverseCheck: function () {</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>// this是jQuery对象</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>this.each(function () {</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>// this是dom元素</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>this.checked = !this.checked</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>return this</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>// 使用, 全选复选框，并将复选框的value更改为111</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>$(':checkbox[name=items]').checkAll().attr("value", "111")</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ul>
<li>注：插件函数最后要 <code>return this</code> 以支持链式调用；</li>
</ul></section>
<section><h2>多库共存<a href="#多库共存"><span>#</span></a></h2><ul>
<li>
<p>问题 : 如果有2个库都有$, 就存在冲突</p>
</li>
<li>
<p>解决 : jQuery库可以释放$的使用权, 让另一个库可以正常使用, 此时jQuery库只能使用jQuery了</p>
</li>
<li>
<p>API : <code>jQuery.noConflict()</code></p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>//自定义js文件</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>(function () {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>window.$ = function () {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>console.log('my lib $()...')</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>})()</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>//调用</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>// 释放$的使用权</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>jQuery.noConflict()</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>// 调用myLib中的$</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>$()</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>// 要想使用jQuery的功能, 只能使用jQuery</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>jQuery(function () {</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>console.log('文档加载完成')</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-dong-hua/</id>
      <title type="text">jQuery动画</title>
      <published>2021-10-24T23:11:29.000Z</published>
      <updated>2021-10-24T23:11:29.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-dong-hua/"/>
      <summary type="text">滑动动画 : 通过不断改变元素的高度(height)实现 slideDown() : 带动画的展开 slideUp() : 带动画的收缩 slideToggle() : 带动画的切换展开/收缩</summary>
      <content type="html"><![CDATA[<section><h2>slideUp / slideDown<a href="#slideup--slidedown"><span>#</span></a></h2><p><strong>滑动动画</strong>: 通过不断改变元素的高度(height)实现</p><ul>
<li>
<p><code>slideDown()</code>: 带动画的展开</p>
</li>
<li>
<p><code>slideUp()</code>: 带动画的收缩</p>
</li>
<li>
<p><code>slideToggle()</code>: 带动画的切换展开/收缩</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const div = $("#test-slide");</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>div.slideUp(3000); // 在3秒钟内逐渐向上消失</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>时间以毫秒为单位，但也可以是 ‘slow(600)’，‘normal(400)’，‘fast(200)’ 这些字符串，无参调用就是’normal’</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const div = $("#test-show-hide");</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>div.slideDown("slow"); // 在600ms内逐渐滑出</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>fadeIn / fadeOut<a href="#fadein--fadeout"><span>#</span></a></h2><p><strong>淡入淡出</strong>: 不断改变元素的透明度(opacity)来实现的</p><ul>
<li>
<p><code>fadeIn()</code>: 带动画的显示</p>
</li>
<li>
<p><code>fadeOut()</code>: 带动画隐藏</p>
</li>
<li>
<p><code>fadeToggle()</code>: 带动画切换显示/隐藏</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const div = $('#test-fade');</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>div.fadeOut('slow'); // 在 0.6 秒内淡出</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>fadeOut('slow')  fadeIn('slow')  fadeToggle('slow')</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section>
<section><h2>show / hide<a href="#show--hide"><span>#</span></a></h2><p><strong>显示隐藏</strong>，默认没有动画, 动画通过改变(opacity/height/width)</p><ul>
<li>
<p><code>show()</code>: (不)带动画的显示</p>
</li>
<li>
<p><code>hide()</code>: (不)带动画的隐藏</p>
</li>
<li>
<p><code>toggle()</code>: (不)带动画的切换显示/隐藏</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const div = $("#test-show-hide");</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>div.hide(3000); // 在3秒钟内逐渐消失</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>div.show("slow"); // 在600ms内逐渐显示</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section>
<section><h2>自定义动画<a href="#自定义动画"><span>#</span></a></h2><p>jQuery动画本质 : 在指定时间内不断改变元素样式值来实现的</p><ul>
<li><code>animate()</code>: 自定义动画效果的动画</li>
<li><code>stop()</code>: 停止动画</li>
</ul><p><code>animate()</code>，它可以实现任意动画效果，我们需要传入的参数就是 DOM 元素<strong>最终的 CSS 状态</strong>和<strong>时间</strong>，jQuery 在时间段内不断调整 CSS 直到达到我们设定的值:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const $div = $("#test-animate");</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>//3s内透明度变为0.25，宽高变为256px</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>$div.animate(</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>opacity: 0.25,</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>width: "256px",</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>height: 256, //此处也可不写单位，传入数值256，单位默认为px</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>},3000)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>通过链式调用能做到动画实现的先后顺序</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const $div = $("#test-animate")</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>//宽度先变为200px，高度再变为200px</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$div.animate({</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>width: 200</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>}, 1000)</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>.animate({</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>height: 200</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>}, 1000)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>animate()</code> 还可以再传入一个回调函数，当动画结束时，该函数将被调用</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const $div = $("#test-animate");</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>$div.animate(</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>opacity: 0.25,</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>width: 256,</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>height: 256</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>3000,</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>function () {</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>console.log("动画已结束")</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>// 恢复至初始状态:</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>$(this).css("opacity", "1.0")</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>.css("width", "128px")</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>.css("height", "128px")</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-shi-jian/</id>
      <title type="text">jQuery事件</title>
      <published>2021-10-21T09:46:14.000Z</published>
      <updated>2021-10-21T09:46:14.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-shi-jian/"/>
      <summary type="text">因为 JavaScript 在浏览器中以单线程模式运行，页面加载后，一旦页面上所有的 JavaScript 代码被执行完后，就只能依赖触发事件来执行 JavaScript 代码。 浏览器在接收到用户的鼠标或键盘输入后，会自动在对应的 DOM 节点上触发相应的事件。 如果该节点已经绑定了对应的 Jav…</summary>
      <content type="html"><![CDATA[<p>因为 JavaScript 在浏览器中以单线程模式运行，页面加载后，一旦页面上所有的 JavaScript 代码被执行完后，就只能依赖触发事件来执行 JavaScript 代码。
浏览器在接收到用户的鼠标或键盘输入后，会自动在对应的 DOM 节点上触发相应的事件。
如果该节点已经绑定了对应的 JavaScript 处理函数，该函数就会自动调用。</p>
<section><h2>事件绑定<a href="#事件绑定"><span>#</span></a></h2><p>事件绑定通过<code>eventName(function(){})</code> 与 <code>on(eventName, funcion(){})</code> 两种方法实现</p><ul>
<li>
<p><code>eventName(function(){})</code> 绑定对应事件名的监听,	例如：<code>$('#div').click(function(){})</code></p>
</li>
<li>
<p><code>on(eventName, funcion(){})</code> 通用的绑定事件监听, 例如：<code>$('#div').on('click', function(){})</code></p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* HTML:</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>*</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* &lt;a id="test-link" href="#0"&gt;点我试试&lt;/a&gt;</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 获取超链接的jQuery对象:</span></div></div><div><div><div>8</div></div><div><span>const $a = $("#test-link")</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>$a.on("click", function () {</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>alert("Hello!")</span></div></div><div><div><div>12</div></div><div><span>})</span></div></div><div><div><div>13</div></div><div><span>$a.click(function () {</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>alert("Hello!")</span></div></div><div><div><div>15</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section>
<section><h2>鼠标事件<a href="#鼠标事件"><span>#</span></a></h2><ul>
<li>
<p><code>click()</code>: 鼠标单击时触发；</p>
</li>
<li>
<p><code>dblclick</code>(): 鼠标双击时触发；</p>
</li>
<li>
<p><code>mouseenter()</code>: 鼠标进入时触发；</p>
</li>
<li>
<p><code>mouseleave()</code>: 鼠标移出时触发；</p>
</li>
<li>
<p><code>mousemove()</code>: 鼠标在 DOM 内部移动时触发；</p>
</li>
<li>
<p><code>hover()</code>: 鼠标进入和退出时触发两个函数，相当于 <code>mouseenter</code> 加上 <code>mouseleave</code></p>
</li>
<li>
<p>注如何区别<code>mouseover()</code>与<code>mouseenter()</code>?</p>
<ul>
<li><code>mouseover()</code>: 在移入子元素时也会触发, 对应mouseout</li>
<li><code>mouseenter()</code>: 只在移入当前元素时才触发, 对应mouseleave</li>
<li><code>hover()</code>使用的就是<code>mouseenter()</code>和<code>mouseleave()</code>，故在使用<code>hover()</code>鼠标移入移出子元素不会触发事件</li>
</ul>
</li>
</ul></section>
<section><h2>键盘事件<a href="#键盘事件"><span>#</span></a></h2><p>键盘事件仅作用在当前焦点的 DOM 上，通常是 <code>&lt;input&gt;</code> 和 <code>&lt;textarea&gt;</code></p><ul>
<li><code>keydown()</code>: 键盘按下时触发</li>
<li><code>keyup()</code>: 键盘松开时触发</li>
<li><code>keypress()</code>: 按一次键后触发</li>
</ul></section>
<section><h2>其他事件<a href="#其他事件"><span>#</span></a></h2><ul>
<li>
<p><code>focus()</code>: 当 DOM 获得焦点时触发</p>
</li>
<li>
<p><code>blur()</code>: 当 DOM 失去焦点时触发</p>
</li>
<li>
<p><code>change()</code>: 当 <code>&lt;input&gt;</code>、<code>&lt;select&gt;</code> 或 <code>&lt;textarea&gt;</code> 的内容改变时触发</p>
</li>
<li>
<p><code>submit()</code>: 当</p>提交时触发<p></p>
</li>
<li>
<p><code>ready()</code>: 当页面被载入并且 DOM 树完成初始化后触发</p>
</li>
<li>
<p>注</p>
<ul>
<li><code>reday()</code>事件ready 仅作用于 document 对象。由于 ready 事件在 DOM 完成初始化后触发，且只触发一次，所以非常适合用来写其他的初始化代码</li>
<li><code>$(document).ready() </code>的简写方式为<code>$(function(){})</code></li>
</ul>
</li>
</ul></section>
<section><h2>事件参数<a href="#事件参数"><span>#</span></a></h2><p>有些事件，如 <code>mousemove()</code> 和 <code>keypress()</code>，我们需要获取鼠标位置和按键的值。所有事件都会传入 <code>Event</code> 对象作为参数，可以从 <code>Event</code> 对象上获取到更多的信息</p><ul>
<li>事件的坐标
<ul>
<li><code>event.clientX</code>, <code>event.clientY</code>  相对于视口的左上角</li>
<li><code>event.pageX</code>, <code>event.pageY</code>  相对于页面的左上角</li>
<li><code>event.offsetX</code>, <code>event.offsetY</code> 相对于事件元素左上角</li>
</ul>
</li>
<li>事件相关处理
<ul>
<li>
<p>停止事件冒泡 : <code>event.stopPropagation()</code></p>
</li>
<li>
<p>阻止事件默认行为 : <code>event.preventDefault()</code></p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//获取当前鼠标相对于页面的位置</span></div></div><div><div><div>2</div></div><div><span>$(function () {</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>$("#testMouseMoveDiv").mousemove(function (event) {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>$("#testMouseMoveSpan").text("pageX = " +</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>event.pageX + ", pageY = " + event.pageY)</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul></section>
<section><h2>取消绑定事件<a href="#取消绑定事件"><span>#</span></a></h2><ul>
<li><code>off(eventName)</code> 取消事件的绑定，</li>
<li>注
<ul>
<li>
<p><code>off('click', function () {...})</code> 不能取消第一个绑定的匿名函数，即使它们写法相同也不是同一个对象</p>
</li>
<li>
<p><code>$("*").off()</code>能够清除所有事件</p>
<p>//点击btn1解除.inner上的所有事件监听
<span>('#btn1').click(function () {
  	</span>(‘.inner’).off()
})</p>
<p>//点击btn2解除.inner上的mouseenter事件
<span>('#btn2').click(function () {
  	</span>(‘.inner’).off(‘mouseenter’)
})</p>
</li>
</ul>
</li>
</ul></section>
<section><h2>事件委托<a href="#事件委托"><span>#</span></a></h2><ul>
<li>
<p>事件委托是指将多个子元素(如li)的事件监听委托给父辈元素(ul)处理，通俗来说 委托方子元素（li）相当于业主，被委托方父元素（ul）相当于中介</p>
<ul>
<li>监听回调是加在了父辈元素上</li>
<li>当操作任何一个子元素(li)时, 事件会冒泡到父辈元素(ul)</li>
<li>父辈元素不会直接处理事件, 而是根据<code>event.target</code>得到发生事件的子元素(li), 通过这个子元素调用事件回调函数</li>
</ul>
</li>
<li>
<p>使用事件委托的好处</p>
<ul>
<li>添加新的子元素, 自动有事件响应处理</li>
<li>能减少事件监听的数量</li>
</ul>
</li>
</ul><blockquote><p>在jQuery 3.0中以弃用，使用 <code>on()</code> 与<code>off()</code>代替</p></blockquote><ul>
<li>设置事件委托: <code>$(parentSelector).delegate(childrenSelector, eventName, callback)</code></li>
<li>移除事件委托: <code>$(parentSelector).undelegate(eventName)</code>
// 设置事件委托
<span><span>(′ul′).delegate(′li′,′click′,function()//console.log(this)//this是当前触发click事件的对象（li）this.style.background=′red′)//向ul中新增li('ul').delegate('li', 'click', function () {
  		// console.log(this) 
  		//this是当前触发click事件的对象（li）
  		this.style.background = 'red'
  	})
  	// 向ul中新增li
  	</span><span><span><span></span><span><span>(</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>u</span><span><span>l</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>)</span><span>.</span><span>d</span><span>e</span><span>l</span><span>e</span><span>g</span><span>a</span><span>t</span><span>e</span><span><span>(</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>l</span><span><span>i</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span><span>,</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span></span><span>c</span><span>l</span><span>i</span><span>c</span><span><span>k</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>,</span><span></span><span>f</span><span>u</span><span>n</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>(</span><span>)</span><span><span>//</span><span>co</span><span>n</span><span>so</span><span>l</span><span>e</span><span>.</span><span>l</span><span>o</span><span>g</span><span>(</span><span>t</span><span>hi</span><span>s</span><span>)</span><span>//</span><span>t</span><span>hi</span><span>s</span><span>是当前触发</span><span>c</span><span>l</span><span>i</span><span>c</span><span>k</span><span>事件的对象（</span><span>l</span><span>i</span><span>）</span><span>t</span><span>hi</span><span>s</span><span>.</span><span>s</span><span>t</span><span>y</span><span>l</span><span>e</span><span>.</span><span>ba</span><span>c</span><span>k</span><span>g</span><span>r</span><span>o</span><span>u</span><span>n</span><span>d</span><span></span><span><span>=</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span></span><span>r</span><span>e</span><span><span>d</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span></span><span>)</span><span>//</span><span>向</span><span>u</span><span>l</span><span>中新增</span><span>l</span><span>i</span></span></span></span>(‘#btn1’).click(function () {
<span>('ul').append('&lt;li&gt;新增的li....&lt;/li&gt;')
  	})
  	</span>(‘#btn2’).click(function () {
// 移除事件委托
$(‘ul’).undelegate(‘click’)
})</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-xiu-gai-dom-jie-gou/</id>
      <title type="text">jQuery修改DOM结构</title>
      <published>2021-10-21T06:26:54.000Z</published>
      <updated>2021-10-21T06:26:54.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-xiu-gai-dom-jie-gou/"/>
      <summary type="text">直接使用浏览器提供的 API 对 DOM 结构进行修改，不但代码复杂，而且要针对浏览器写不同的代码。 有了 jQuery，我们就专注于操作 jQuery 对象本身，底层的 DOM 操作由 jQuery 完成就可以了，这样一来，修改 DOM 也大大简化了</summary>
      <content type="html"><![CDATA[<section><h1>修改DOM结构<a href="#修改dom结构"><span>#</span></a></h1><p>直接使用浏览器提供的 API 对 DOM 结构进行修改，不但代码复杂，而且要针对浏览器写不同的代码。 有了 jQuery，我们就专注于操作 jQuery 对象本身，底层的 DOM 操作由 jQuery 完成就可以了，这样一来，修改 DOM 也大大简化了</p><section><h2>添加DOM节点<a href="#添加dom节点"><span>#</span></a></h2><p>要添加新的 DOM 节点，除了通过 jQuery 的 html() 这种暴力方法外，还可以用 append() 方法，例如在<code>&lt;ul&gt;</code>内部插入内容</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;div id="test-div"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;ul&gt;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>&lt;li&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>&lt;li&gt;&lt;span&gt;Python&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>&lt;li&gt;&lt;span&gt;Swift&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>首先要拿到<code>&lt;ul&gt;</code>节点：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const $ul = $("#test-div&gt;ul")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>调用 <code>append()</code> 传入 HTML 片段:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$ul.append("&lt;li&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;/li&gt;")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也可以使用<code>appendTo()</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$("&lt;li&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;/li&gt;").appendTo($ul)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>append()</code> 把 DOM 添加到最后，而<code>prepend()</code> 则把 DOM 添加到最前，<code>prependTo()</code>同理</p><p>如果要把新节点插入到指定位置，例如，JavaScript 和 Python 之间，那么，可以先定位到 JavaScript，然后用 after() 方法:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const js = $("#test-div&gt;ul&gt;li:first-child");</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>js.after("&lt;li&gt;&lt;span&gt;Lua&lt;/span&gt;&lt;/li&gt;");</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也就是说，同级节点可以用 after() 或者 before() 方法。</p></section><section><h2>删除DOM节点<a href="#删除dom节点"><span>#</span></a></h2><ul>
<li><code>empty()</code>删除所有匹配元素的子元素，不会删除自身节点</li>
<li><code>remove()</code>删除所有匹配的元素，包括自己</li>
</ul><p>例如：<code>empty()</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var $li = $("#test-div&gt;ul li")</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>$li.empty()</span></div></div><div><div><div>3</div></div><div><span><span>   </span></span><span>//删除后</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;div id="test-div"&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;ul&gt;</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>&lt;li&gt;&lt;/li&gt;</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>&lt;li&gt;&lt;/li&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;li&gt;&lt;/li&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>而<code>remove()</code>方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$li.remove()</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>//删除后</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;div id="test-div"&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;ul&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-cao-zuo-dom-yuan-su/</id>
      <title type="text">jQuery操作DOM元素</title>
      <published>2021-10-21T05:40:59.000Z</published>
      <updated>2021-10-21T05:40:59.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-cao-zuo-dom-yuan-su/"/>
      <summary type="text">jQuery对象提供了许多修改DOM元素的功能</summary>
      <content type="html"><![CDATA[<section><h1>jQuery操作DOM<a href="#jquery操作dom"><span>#</span></a></h1><p>jQuery对象提供了许多修改DOM元素的功能</p><section><h2>修改Text和HTML<a href="#修改text和html"><span>#</span></a></h2><p>jQuery 对象的 text() 和 html() 方法分别获取节点的文本和原始 HTML 文本，例如，如下的 HTML 结构:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;!-- HTML结构 --&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;ul id="test-ul"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;li class="js"&gt;111&lt;/li&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;li name="book"&gt;&lt;span&gt;222&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/ul&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>分别获取文本和 HTML:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log($("#test-ul li[name=book]").text()); // 222</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log($("#test-ul li[name=book]").html()); // &lt;span&gt;222&lt;/span&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如jQuery 的 API 实现了读写合一的功能，也就是: 无参数调用 text() 是获取文本，传入参数就变成设置文本，HTML 也是类似操作
例如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>$("#test-ul li[name=book]").text("aaa")</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>修改CSS<a href="#修改css"><span>#</span></a></h2><ul>
<li>jQuery 对象有“批量操作”的特点，对获取的DOM元素集进行操作</li>
</ul><p>例如下面的结构</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;!-- HTML结构 --&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;ul id="test-css"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;li class="lang dy"&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;li class="lang"&gt;&lt;span&gt;Java&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;li class="lang dy"&gt;&lt;span&gt;Python&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;li class="lang"&gt;&lt;span&gt;Swift&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;li class="lang dy"&gt;&lt;span&gt;Scheme&lt;/span&gt;&lt;/li&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;/ul&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以用调用 jQuery 对象的 <code>css('name', 'value')</code> 方法，更改css样式。而获取css样式则是<code>css('name')</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//更改li的class为dy的span元素</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>$("#test-css li.dy&gt;span")</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>.css("background-color", "#bfa")</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>.css("color", "red")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>同时jQuery提供了修改css类的方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const div = $("#test-div");</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>div.hasClass("highlight"); // false， class是否包含highlight</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>div.addClass("highlight"); // 添加highlight这个class</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>div.removeClass("highlight"); // 删除highlight这个class</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>显示和隐藏 DOM<a href="#显示和隐藏-dom"><span>#</span></a></h2><ul>
<li>
<p>jQuery 直接提供show()和hide()方法</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const a = $("a");</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a.hide(); // 隐藏</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>a.show(); // 显示</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>注意，隐藏 DOM 节点并未改变 DOM 树的结构，它只影响 DOM 节点的显示。这和删除 DOM 节点是不同的。</p></section><section><h2>获取 DOM 信息<a href="#获取-dom-信息"><span>#</span></a></h2><ul>
<li>
<p>利用 jQuery 对象的若干方法，我们直接可以获取 DOM 的高宽等信息，而无需针对不同浏览器编写特定代码:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// 浏览器可视窗口大小:</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>$(window).width(); // 800</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>$(window).height(); // 600</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>// HTML文档大小:</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>$(document).width(); // 800</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>$(document).height(); // 3500</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>// 某个div的大小:</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>const div = $("#test-div");</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>div.width(); // 600</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>div.height(); // 300</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>div.width(400); // 设置CSS属性 width: 400px，是否生效要看CSS是否有效</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>div.height("200px"); // 设置CSS属性 height: 200px，是否生效要看CSS是否有效</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><code>attr()</code> 和 <code>removeAttr()</code> 方法用于操作 DOM 节点的属性:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>// &lt;div id="test-div" name="Test"&gt;...&lt;/div&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>const div = $("#test-div");</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>div.attr("data"); // undefined, 属性不存在</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>div.attr("name"); // 'Test'</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>div.attr("name", "Hello"); // div的name属性变为'Hello'</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>div.removeAttr("name"); // 删除name属性</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>div.attr("name"); // undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>prop() 方法和 attr() 类似，但是 HTML5 规定有一种属性在 DOM 节点中可以没有值，只有出现与不出现两种，例如:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;input id="test-radio" type="radio" name="test" checked value="1" /&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>等价于:

<code>attr()</code> 和 <code>prop()</code> 对于属性 checked 处理有所不同:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>const radio = $("#test-radio");</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>radio.attr("checked"); // 'checked'</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>radio.prop("checked"); // true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>一般来说
- attr(): 操作属性值为非布尔值的属性
- prop(): 专门操作属性值为布尔值的属性</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-xuan-ze-qi/</id>
      <title type="text">jQuery选择器</title>
      <published>2021-10-19T19:55:03.000Z</published>
      <updated>2021-10-19T19:55:03.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-xuan-ze-qi/"/>
      <summary type="text">jQuery 选择器允许对 HTML 元素组或单个元素进行操作。 jQuery 选择器基于元素的 id、类、类型、属性、属性值等&quot;查找&quot;（或选择）HTML 元素。 它基于已经存在的 CSS 选择器，除此之外，它还有一些自定义的选择器。 jQuery 中所有选择器都以美元符号开头：$()。 jQuer…</summary>
      <content type="html"><![CDATA[<ul>
<li>jQuery 选择器允许对 HTML 元素组或单个元素进行操作。</li>
<li>jQuery 选择器基于元素的 id、类、类型、属性、属性值等”查找”（或选择）HTML 元素。 它基于已经存在的 CSS 选择器，除此之外，它还有一些自定义的选择器。</li>
<li>jQuery 中所有选择器都以美元符号开头：$()。</li>
<li>jQuery选择器不是同时执行，而是顺序执行</li>
</ul>
<section><h2>基本选择器<a href="#基本选择器"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>$("#test")             　　　　   选择id值为test的元素，id值是唯一的所以返回单个元素。</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>$("div")               　　　　   选择所有的div标签元素，返回div元素数组</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>$(".myclass")          　　　　   选择使用myclass类的css的所有元素</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>$("*")                 　　　　   选取所有元素。</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>$("#test,div,.myclass")      选取多个元素。(并集)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>层次选择器<a href="#层次选择器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("div span")   选取 &lt; div &gt; 里的所有 &lt; span &gt; 元素</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$("div &gt;span")   选取 &lt; div &gt; 元素下元素名是 &lt; span &gt; 的子元素</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$("#one +div")   选取id为one的元素的下一个 &lt; div &gt; 同辈元素 等同于$("#one").next("div")</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>$("#one~div")   选取id为one的元素的元素后面的所有 &lt; div &gt; 同辈元素 等同于$("#one").nextAll("div")</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>$("#one").siblings("div")   获取id为one的元素的所有 &lt; div &gt; 同辈元素（ 不管前后）</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>$("#one").prev("div")   获取id为one的元素的前面紧邻的同辈 &lt; div &gt; 元素</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>所以 获取元素范围大小顺序依次为：</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>$("#one").siblings("div") &gt; $("#one~div") &gt; $("#one +div") 或是</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>$("#one").siblings("div") &gt; $("#one").nextAll("div") &gt; $("#one").next("div")</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>基本过滤选择器<a href="#基本过滤选择器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("div:first") 选取所有 &lt; div &gt; 元素中第1个 &lt; div &gt; 元素</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$("div:last") 选取所有 &lt; div &gt; 元素中最后一个 &lt; div &gt; 元素</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$(":not(.myClass)") 选取class不是myClass的元素</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>$(":even") 选取索引是偶数的元素(索引从0开始)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>$(":odd") 选取索引是奇数的元素(索引从0开始)</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>$(":eq(index)") 选取索引等于index的元素</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>$(":gt(index)") 选取索引大于index的元素</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>$(":lt(index)") 选取索引小于index的元素</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>$(":header") 过滤掉所有标题元素， 例如： h1、 h2、 h3等</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>$(":animated") 选取正在执行动画的元素</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>$(":focus") 选取当前获取焦点的元素</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ul>
<li>例子：选取一堆div元素中的<strong>第二个和第三个div元素</strong>， <code>$("div:gt(0):lt(2)")</code> ，因为jQuery选择器不是同时执行，而是顺序执行，<code>div:gt(0)</code>后此时选中的是整个div中第2个元素，而 <code>:lt(2)</code> 是基于<code>div:gt(0)</code>执行后的DOM元素集，当然也能写成 <code>$("div:lt(3):gt(0)")</code></li>
</ul></section>
<section><h2>内容过滤选择器<a href="#内容过滤选择器"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("div:contains('Name')") 选取所有 &lt; div &gt; 中含有 'Name'</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>文本的元素</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>$("div:empty") 选取不包含子元素（ 包括文本元素） 的 &lt; div &gt; 空元素</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>$("div:has(p)") 选取所有含有 &lt; p &gt; 元素的 &lt; div &gt; 元素</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>$("div:parent") 选取拥有子元素的（ 包括文本元素） &lt; div &gt; 元素</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>可见性过滤选择器<a href="#可见性过滤选择器"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("div:hidden") 选取所有不可见的 &lt; div &gt; 元素</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$("div:visible") 选取所有可见的 &lt; div &gt; 元素</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>属性过滤选择器<a href="#属性过滤选择器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("div[id]") 选取所有拥有属性id的元素</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>$("input[name='test']") 选取所有的name属性等于 'test'的 &lt; input &gt; 元素</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>$("input[name!='test']") 选取所有的name属性不等于 'test'的 &lt; input &gt; 元素</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>$("input[name^='news']") 选取所有的name属性以 'news'开头的 &lt; input &gt; 元素</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>$("input[name$='news']") 选取所有的name属性以 'news'结尾的 &lt; input &gt; 元素</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>$("input[name*='news']") 选取所有的name属性包含 'news'的 &lt; input &gt; 元素</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>$("div[title|='en']") 选取属性title等于 'en'或以 'en'为前缀(该字符串后跟一个连字符 '-') 的 &lt; div &gt; 元素</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>$("div[title~='en']") 选取属性title用空格分隔的值中包含字符en的 &lt; div &gt; 元素</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>$("div[id][title$='test']") 选取拥有属性id, 并且属性title以 'test'结束的 &lt; div &gt; 元素</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>子元素过滤选择器<a href="#子元素过滤选择器"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("div .one:nth-child(2)") 选取class为 'one' 的 &lt; div &gt; 父元素下的第2个子元素</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$("div span:first-child") 选取每个 &lt; div &gt; 中的第1个 &lt; span &gt; 元素</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$("div span:last-child") 选取每个 &lt; div &gt; 中的最后一个 &lt; span &gt; 元素</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>$("div button:only-child") 在 &lt; div &gt; 中选取是唯一子元素的 &lt; button &gt; 元素</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>表单对象属性过滤选择器<a href="#表单对象属性过滤选择器"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$("#form1 :enabled") 选取id为 'form1'的表单内所有可用元素</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$("#form2 :disabled") 选取id为 'form2'的表单内所有不可用元素</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$("input :checked") 选取所有被选中的 &lt; input &gt; 元素</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>$("select option:selected") 选取所有的select 的子元素中被选中的元素</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>表单选择器<a href="#表单选择器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>$(":input")  选取所有 &lt; input &gt; , &lt; textarea &gt; , &lt; select &gt; 和 &lt; button &gt; 元素</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$(":text")  选取所有的单行文本框</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$(":password")  选取所有的密码框</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>$(":radio")  选取所有单的选框</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>$(":checkbox")  选取所有的多选框</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>$(":submit")  选取所有的提交按钮</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>$(":image")  选取所有的图像按钮</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>$(":reset")  选取所有的重置按钮</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>$(":button")  选取所有的按钮</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>$(":file")  选取所有的上传域</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>$(":hidden")  选取所有不可见元素</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/jquery-jie-shao/</id>
      <title type="text">jQuery介绍</title>
      <published>2021-10-19T09:03:28.000Z</published>
      <updated>2021-10-19T09:03:28.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/jquery-jie-shao/"/>
      <summary type="text">jQuery 是 JavaScript 世界中使用最广泛的一个库。鼎盛时期全世界大约有 80 90%的网站直接或间接地使用了 jQuery。</summary>
      <content type="html"><![CDATA[<p>jQuery 是 JavaScript 世界中使用最广泛的一个库。鼎盛时期全世界大约有 80~90%的网站直接或间接地使用了 jQuery。</p>
<ul>
<li>jQuery 能帮我们干这些事情:
<ul>
<li>消除浏览器差异: 您不需要自己写冗长的代码来针对不同的浏览器来绑定事件，编写 AJAX 等代码；</li>
<li>简洁的操作 DOM 的方法: 写 $(‘#test’) 肯定比 document.getElementById(‘test’) 来得简洁； 轻松实现动画、修改 CSS 等各种操作。</li>
<li>jQuery 的理念 “Write Less, Do More“，让您写更少的代码，完成更多的工作！</li>
</ul>
</li>
</ul>
<section><h2>jQuery版本<a href="#jquery版本"><span>#</span></a></h2><ul>
<li>jQuery 的 1.x 版本 兼容 ie678，2.x 为了精简代码体积移除了 ie678 的兼容。3.x 只兼容最新的一批浏览器</li>
<li>从jQuery官网 <a href="https://jquery.com/" target="_blank">https://jquery.com/</a> 下载最新版 3.x 即可</li>
</ul></section>
<section><h2>jQuery的本质<a href="#jquery的本质"><span>#</span></a></h2><ul>
<li>
<p>jQuery实际上是封装好的js文件，只向外暴露了 $ / jQuery</p>
</li>
<li>
<p>整个文件相当于下段代码
(function (window) {
var jQuery = function () {
return new xxx()
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>      </span></span><span>window.$ = window.jQuery = jQuery</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>})(window)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section>
<section><h2>使用jQuery<a href="#使用jquery"><span>#</span></a></h2><ul>
<li>jQuery实际上是js文件，因此使用 jQuery 是需要先从外部引入js文件</li>
</ul><section><h3>本地引入<a href="#本地引入"><span>#</span></a></h3><ul>
<li>将jQuery文件放在本地文件内
</li>
</ul></section><section><h3>远程引入<a href="#远程引入"><span>#</span></a></h3><ul>
<li>从外部CDN库中引入，例如免费开源网站 <a href="https://www.bootcdn.cn/" target="_blank">https://www.bootcdn.cn/</a>
</li>
</ul></section></section>
<section><h2>jQuery核心函数<a href="#jquery核心函数"><span>#</span></a></h2><ul>
<li>
<p>jQuery函数在代码中写作 $ 或 jQuery</p>
</li>
<li>
<p>jQuery库向外直接暴露的就是 $ / jQuery</p>
</li>
<li>
<p>引入jQuery库后, 直接使用$即可</p>
<ul>
<li>当函数用: $(xxx)</li>
<li>当对象用: $.xxx()</li>
</ul>
<p>console.log(typeof <span><span>)//functionconsole.log(jQuery===) // function
  console.log(jQuery === </span><span><span><span></span><span>)</span><span>//</span><span>f</span><span>u</span><span>n</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>co</span><span>n</span><span>so</span><span>l</span><span>e</span><span>.</span><span>l</span><span>o</span><span>g</span><span>(</span><span>j</span><span>Q</span><span>u</span><span>er</span><span>y</span><span></span><span>===</span></span></span></span>) // true</p>
</li>
</ul><section><h3>作为一般函数调用<a href="#作为一般函数调用"><span>#</span></a></h3><ul>
<li>
<p>作为一般函数调用: $(param)</p>
<ol>
<li>参数为函数 : 当DOM加载完成后，执行此回调函数，相当于<code>window.onload = function(){}</code></li>
<li>参数为选择器字符串: 查找所有匹配的标签, 并将它们封装成jQuery对象</li>
<li>参数为DOM对象: 将dom对象封装成jQuery对象</li>
<li>参数为html标签字符串 (用得少): 创建标签对象并封装成jQuery对象</li>
</ol>
<p>// html部分
</p><div>
测试
<br />
</div>
//js部分
$(function () { // 绑定文档加载完成的监听<p></p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>      </span></span><span>// 1.2). 参数为选择器字符串: 查找所有匹配的标签, 并将它们封装成jQuery对象</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>$('#btn').click(function () { // 绑定点击事件监听</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>// 1.3). 参数为DOM对象: 将dom对象封装成jQuery对象</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>alert($(this).html()) // this是什么? 发生事件的dom元素(&lt;button&gt;)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>          </span></span><span>// 1.4). 参数为html标签字符串 (用得少): 创建标签对象并封装成jQuery对象</span></div></div><div><div><div>8</div></div><div><span><span>          </span></span><span>$('&lt;input type="text" name="msg"/&gt;&lt;br/&gt;').appendTo('div')</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>在上面代码中，需求是 <strong>点击按钮: 显示按钮的文本, 显示一个新的输入框</strong></p>
</li>
</ul></section><section><h3>作为对象使用<a href="#作为对象使用"><span>#</span></a></h3><ul>
<li>作为对象调用：$.xxx()，例如
<ol>
<li>
<p>$.each() : 隐式遍历数组</p>
</li>
<li>
<p>$.trim() : 去除两端的空格</p>
<p>//需求： 遍历输出数组中所有元素值
var arr = [2, 4, 7]
// 1). $.each() : 隐式遍历数组</p>
<p>$.each(arr, function (index, item) {
console.log(index, item)
})</p>
</li>
</ol>
</li>
</ul></section></section>
<section><h2>jQuery核心对象<a href="#jquery核心对象"><span>#</span></a></h2><ul>
<li>jQuery对象是一个<strong>包含所有匹配的任意多个dom元素的伪数组对象</strong></li>
<li>如何得到jQuery对象: 执行jQuery函数返回的就是jQuery对象</li>
<li>使用jQuery对象: $obj.xxx()</li>
<li>基本行为：
<ul>
<li>
<p>length: 得到DOM元素的个数</p>
</li>
<li></li>
<li>
<p>each(function(index, domEle){}): 遍历所有的DOM元素</p>
</li>
<li>
<p>index(): 得到当前DOM元素在所有兄弟元素中的下标</p>
<p>console.log($() instanceof Object) // true</p>
</li>
</ul>
</li>
</ul><ul>
<li>例如下段代码
//html部分
测试一
测试二
测试三
测试四
//js部分
<span>(function () {
              var </span>buttons = <span><span>("button")console.log(("button")
              console.log(</span><span><span><span></span><span>(</span><span>"</span><span>b</span><span>u</span><span>tt</span><span>o</span><span>n</span><span>"</span><span>)</span><span>co</span><span>n</span><span>so</span><span>l</span><span>e</span><span>.</span><span>l</span><span>o</span><span>g</span><span>(</span></span></span></span>buttons.length) //4
console.log($buttons[2].innerHTML) //测试三
})
<ul>
<li>通过调用 $(“button”) 函数 return 一个 jQuery对象，这个对象就是 <code>&lt;button&gt;</code>的DOM元素的伪数组对象</li>
<li>jQuery对象的命名一般以 <span><span>开头，如开头，如</span><span><span><span></span><span>开头，如</span></span></span></span>xxx</li>
</ul>
</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/xian-cheng-ji-zhi-yu-shi-jian-ji-zhi/</id>
      <title type="text">线程机制与事件机制</title>
      <published>2021-10-18T08:36:27.000Z</published>
      <updated>2021-10-18T08:36:27.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/xian-cheng-ji-zhi-yu-shi-jian-ji-zhi/"/>
      <summary type="text">进程：程序的一次执行, 它占有一片独有的内存空间 线程： CPU的基本调度单位, 是程序执行的一个完整流程</summary>
      <content type="html"><![CDATA[<section><h2>线程<a href="#线程"><span>#</span></a></h2><ul>
<li>
<p>进程：程序的一次执行, 它占有一片独有的内存空间</p>
</li>
<li>
<p>线程： CPU的基本调度单位, 是程序执行的一个完整流程</p>
</li>
<li>
<p>进程与线程</p>
<ul>
<li>一个进程中一般至少有一个运行的线程: 主线程</li>
<li>一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的</li>
<li>一个进程内的数据可以供其中的多个线程直接共享</li>
<li>多个进程之间的数据是不能直接共享的</li>
</ul>
</li>
</ul><section><h3>浏览器内核<a href="#浏览器内核"><span>#</span></a></h3><ul>
<li>
<p>浏览器内核是指支持浏览器运行的最核心程序
不同浏览器的内核可能不同
Chrome, Safari: webkit
firefox: Gecko
IE: Trident
360,搜狗等国内浏览器: Trident + webkit</p>
</li>
<li>
<p>内核由很多模块组成</p>
<ul>
<li>html,css文档解析模块 : 负责页面文本的解析</li>
<li>dom/css模块 : 负责dom/css在内存中的相关处理</li>
<li>布局和渲染模块 : 负责页面的布局和效果的绘制</li>
<li>布局和渲染模块 : 负责页面的布局和效果的绘制</li>
<li>定时器模块 : 负责定时器的管理</li>
<li>网络请求模块 : 负责服务器请求(常规/Ajax)</li>
<li>事件响应模块 : 负责事件的管理</li>
</ul>
</li>
</ul></section><section><h3>js是单线程的<a href="#js是单线程的"><span>#</span></a></h3><ul>
<li>
<p>JS 是单线程的语言，所谓“单线程”就是一根筋，对于拿到的程序，一行一行的执行，直到上面的执行为完成，只能做这一件事</p>
</li>
<li>
<p>H5提出了实现多线程的方案：Web workers（很少用）</p>
</li>
<li>
<p>只能在主线程中更新界面，分线程中不能</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"1"</span><span>)</span></div></div><div><div><div>2</div></div><div><span>    </span><span>setTimeout</span><span>(</span><span>function</span><span>(){</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}, </span><span>1000</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>3</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>输出：1 3 2</p>
<p>这个任务是串行的，不能发生阻塞，使用异步解决这种问题</p>
<p>过程：</p>
<ol>
<li>执行第一行，打印1</li>
<li>执行setTimeout 后，传入setTimeout 的函数会被暂存起来，不会立即执行（单线程的特点，不能同时干两件事）</li>
<li>执行最后一行，打印3</li>
<li>待所有程序执行完，处于空闲状态时，会立马看有没有暂存起来的要执行</li>
<li>发现暂存起来的 setTimeout 中的函数无需等待时间，立即起来执行</li>
</ol>
<p>而多个异步代码将会进入回调队列，排队执行</p>
<p>如果主线程中程序执行需要200ms，定时器设置定时100ms，最终在整个程序中定时器会在200ms后执行，而不是300ms。因为在100ms时定时器已经进入回调队列，而主线程程序执行完毕时，回调队列中第一个函数（例子中的定时器）会直接进入执行栈中执行。</p>
</li>
</ul></section></section>
<section><h2>事件机制<a href="#事件机制"><span>#</span></a></h2><ul>
<li>所有代码分类
<ul>
<li><strong>初始化执行代码(同步代码)</strong>: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码</li>
<li><strong>回调执行代码(异步代码)</strong>: 处理回调逻辑</li>
</ul>
</li>
<li>js引擎执行代码的基本流程：先执行初始化代码，后面在某个时刻才执行回调代码</li>
</ul><section><h3>事件循环模型<a href="#事件循环模型"><span>#</span></a></h3><ul>
<li>事件循环模型由两个重要部分组成
<ul>
<li>事件（定时器、DOM事件、Ajax）管理模块</li>
<li>回调队列</li>
</ul>
</li>
<li>模型运转流程
<ol>
<li>执行初始化代码, 将事件回调函数交给对应模块管理</li>
<li>当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中</li>
<li>只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行</li>
</ol>
</li>
</ul><p></p><figure><img src="/images/javascript/%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF%E6%A8%A1%E5%9E%8B.png" alt="事件循环模型" /><figcaption>事件循环模型</figcaption></figure><p></p><p>上图中相关重要概念</p><ol>
<li>执行栈（execution stack）：所有代码都是在此空间执行的</li>
<li>浏览器内核（browser core）：js引擎模块(在主线程处理)，其它模块(在主/分线程处理，如定时器、Ajax、DOM事件监听)</li>
<li>回调队列（callback queue）&lt;异步代码在此处等待排队&gt;</li>
<li>事件轮询（event loop）&lt;从回调队列中循环取出回调函数放入执行栈中处理&gt;(一个接一个)</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/dui-xiang/</id>
      <title type="text">对象</title>
      <published>2021-10-18T08:13:23.000Z</published>
      <updated>2021-10-18T08:13:23.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/dui-xiang/"/>
      <summary type="text">对象是JS中的引用数据类型 对象是一种复合数据类型，在对象中可以保存多个不同数据类型的属性 使用 typeof 检查一个对象时，会返回 object</summary>
      <content type="html"><![CDATA[<section><h1>对象（Object）<a href="#对象object"><span>#</span></a></h1><ul>
<li>对象是JS中的引用数据类型</li>
<li>对象是一种复合数据类型，在对象中可以保存多个不同数据类型的属性</li>
<li>使用<code>typeof</code>检查一个对象时，会返回<code>object</code></li>
</ul><section><h2>对象的基本语法<a href="#对象的基本语法"><span>#</span></a></h2><section><h3>创建对象<a href="#创建对象"><span>#</span></a></h3><ul>
<li>
<p>方式一：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var obj = new Object()</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>方式二：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var obj = {}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>方式三：
使用对象字面量，在创建对象时直接向对象中添加属性</p>
<ul>
<li>
<p>语法：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var obj = {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name: 'tom',</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>age: 18,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>sex: '男',</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
<p><strong>注</strong>：在什么时候使用 <code>对象["对象名"]</code> 的方式</p>
<ol>
<li>
<p>属性名包含特殊字符：_ 空格</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//需求: 添加一个属性: content-type: text/json</span></div></div><div><div><div>2</div></div><div><span>//p.content-type = 'text/json' //不正确</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>p['content-type'] = 'text/json'</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>属性名不确定</p>
</li>
</ol>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//将name作为p的属性添加进p对象</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>var prop = 'name'</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>var value = 123</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>// p.prop = value  //不正确</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>p[prop] = value</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>```</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>1. 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见</span></div></div><div><div><div>9</div></div><div><span>2. 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>向对象中添加属性<a href="#向对象中添加属性"><span>#</span></a></h3><ul>
<li>语法：
<code>对象.属性名 = 属性值</code>
<code>对象["属性名"] = 属性值</code>
<ul>
<li>对象的属性名没有任何要求，不需要遵守标识符的规范，
但是在开发中，尽量按照标识符的要求去写。</li>
<li>属性值也可以任意的数据类型。</li>
</ul>
</li>
</ul></section><section><h3>读取对象中的属性<a href="#读取对象中的属性"><span>#</span></a></h3><ul>
<li>
<p>语法：</p>
<p><code>对象.属性名</code>
<code>对象["属性名"]</code></p>
</li>
<li>
<p>如果读取一个对象中没有的属性，它不会报错，而是返回一个<code>undefined</code></p>
</li>
</ul></section><section><h3>删除对象中的属性<a href="#删除对象中的属性"><span>#</span></a></h3><ul>
<li>语法：
<code>delete 对象.属性名</code>
<code>delete 对象["属性名"]</code></li>
</ul></section><section><h3>使用in检查对象中是否含有指定属性<a href="#使用in检查对象中是否含有指定属性"><span>#</span></a></h3><ul>
<li>
<p>语法：
<code>"属性名" in 对象</code></p>
</li>
<li>
<p>如果在对象中含有该属性，则返回true如果没有则返回false</p>
</li>
</ul></section><section><h3>基本数据类型和引用数据类型<a href="#基本数据类型和引用数据类型"><span>#</span></a></h3><ul>
<li>基本数据类型
<code>String Number Boolean Null Undefined</code></li>
<li>引用数据类型
<code>Object</code></li>
<li>基本数据类型的数据，变量是直接保存的它的值。
变量与变量之间是互相独立的，修改一个变量不会影响其他的变量。</li>
<li>引用数据类型的数据，变量是保存的对象的引用（内存地址）。
如果多个变量指向的是同一个对象，此时修改一个变量的属性，会影响其他的变量。</li>
<li>比较两个变量时，对于基本数据类型，比较的就是值，
对于引用数据类型比较的是地址，地址相同才相同</li>
</ul></section></section><section><h2>创建对象的方式<a href="#创建对象的方式"><span>#</span></a></h2><section><h3>Object构造函数模式<a href="#object构造函数模式"><span>#</span></a></h3><ul>
<li>
<p>套路: 先创建空<code>Object</code>对象, 再动态添加属性/方法</p>
</li>
<li>
<p>适用场景: 起始时不确定对象内部数据</p>
</li>
<li>
<p>问题: 语句太多</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>p</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Object</span><span>()</span></div></div><div><div><div>2</div></div><div><span><span>p</span><span>.</span></span><span>name</span><span> </span><span>=</span><span> </span><span>'Tom'</span></div></div><div><div><div>3</div></div><div><span><span>p</span><span>.</span></span><span>age</span><span> </span><span>=</span><span> </span><span>12</span></div></div><div><div><div>4</div></div><div><span><span>p</span><span>.</span></span><span>setName</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>name</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>对象字面量模式<a href="#对象字面量模式"><span>#</span></a></h3><ul>
<li>
<p>套路: 使用<code>{}</code>创建对象, 同时指定属性/方法</p>
</li>
<li>
<p>适用场景: 起始时对象内部数据是确定的</p>
</li>
<li>
<p>问题: 如果创建多个对象, 有重复代码</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>p</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>'Tom'</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>23</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>setName</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>name</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>工厂模式<a href="#工厂模式"><span>#</span></a></h3><ul>
<li>
<p>套路: 通过工厂函数动态创建对象并返回</p>
</li>
<li>
<p>适用场景: 需要创建多个对象</p>
</li>
<li>
<p>问题: 对象没有一个具体的类型, 都是<code>Object</code>类型</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>createPerson</span><span><span>(</span><span>name</span><span>, </span><span>age</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>var</span><span><span> </span><span>p</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>name</span><span>:</span><span><span> </span><span>name</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span>        </span><span>age</span><span>:</span><span><span> </span><span>age</span><span>,</span></span></div></div><div><div><div>5</div></div><div><span>        </span><span>setName</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>name</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>            </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>    </span><span>return</span><span><span> </span><span>p</span></span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>var</span><span><span> </span><span>p1</span><span> </span></span><span>=</span><span> </span><span>createPerson</span><span>(</span><span>'Tom'</span><span>, </span><span>12</span><span>)</span></div></div><div><div><div>12</div></div><div><span>var</span><span><span> </span><span>p2</span><span> </span></span><span>=</span><span> </span><span>createPerson</span><span>(</span><span>'JAck'</span><span>, </span><span>13</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>自定义构造函数模式<a href="#自定义构造函数模式"><span>#</span></a></h3><ul>
<li>
<p>套路: 自定义构造函数, 通过<code>new</code>创建对象</p>
</li>
<li>
<p>适用场景: 需要创建多个类型确定的对象</p>
</li>
<li>
<p>问题: 每个对象都有相同的数据, 浪费内存</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Person</span><span><span>(</span><span>name</span><span>, </span><span>age</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>age</span><span> </span><span>=</span><span><span> </span><span>age</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>setName</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>name</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>        </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>var</span><span><span> </span><span>p1</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>(</span><span>'Tom'</span><span>, </span><span>12</span><span>)</span></div></div><div><div><div>10</div></div><div><span>var</span><span><span> </span><span>p2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>(</span><span>'Tom2'</span><span>, </span><span>13</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>构造函数+原型的组合模式<a href="#构造函数原型的组合模式"><span>#</span></a></h3><ul>
<li>
<p>套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上</p>
</li>
<li>
<p>适用场景: 需要创建多个类型确定的对象</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Person</span><span><span>(</span><span>name</span><span>, </span><span>age</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>age</span><span> </span><span>=</span><span><span> </span><span>age</span></span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>Person</span><span>.</span><span>prototype</span><span>.</span><span>setName</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>name</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>var</span><span><span> </span><span>p1</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>(</span><span>'Tom'</span><span>, </span><span>12</span><span>)</span></div></div><div><div><div>9</div></div><div><span>var</span><span><span> </span><span>p2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>(</span><span>'JAck'</span><span>, </span><span>23</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section></section><section><h2>原型 prototype<a href="#原型-prototype"><span>#</span></a></h2><section><h3>基本介绍<a href="#基本介绍"><span>#</span></a></h3><p>我们所创建的每一个函数，解析器都会向函数中添加一个属性<code>prototype</code>，这个属性对应着一个对象，这个对象就是我们所谓的原型对象
如果函数作为普通函数调用<code>prototype</code>没有任何作用，当函数以构造函数的形式调用时，它所创建的对象中都会有一个隐含的属性，指向该构造函数的原型对象，我们可以通过 <code>__proto__</code> 来访问该属性</p><p>原型对象就相当于一个公共的区域，所有同一个类的实例都可以访问到这个原型对象，我们可以将对象中共有的内容，统一设置到原型对象中。</p><p>当我们访问对象的一个属性或方法时，它会先在对象自身中寻找，如果有则直接使用，如果没有则会去原型对象中寻找，如果找到则直接使用</p><p>我们创建构造函数时，可以将这些对象共有的属性和方法，统一添加到构造函数的原型对象中，这样不用分别为每一个对象添加，也不会影响到全局作用域，就可以使每个对象都具有这些属性和方法了</p><ul>
<li>
<p>向构造函数的原型中添加属性</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>MyClass.prototype.name = 123</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>向构造函数中原型中添加一个方法</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>MyClass</span><span>.</span><span>prototype</span><span>.</span><span>sayHello</span><span> </span><span>=</span><span> </span><span>function</span><span>(){</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"hello"</span><span>)</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3><code>hasOwnProperty()</code><a href="#hasownproperty"><span>#</span></a></h3><ul>
<li>可以使用对象的<code>hasOwnProperty()</code>来检查对象自身中是否含有该属性</li>
<li>使用<code>hasOwnProperty()</code>方法只有当对象自身中含有属性时，才会返回<code>true</code></li>
<li>使用<code>in</code>检查对象中是否含有某个属性时，如果对象中没有但是原型中有，也会返回<code>true</code></li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>Person</span><span><span>(</span><span>name</span><span>, </span><span>age</span><span>, </span><span>gender</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>age</span><span> </span><span>=</span><span><span> </span><span>age</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>gender</span><span> </span><span>=</span><span><span> </span><span>gender</span></span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>Person</span><span>.</span><span>prototype</span><span>.</span><span>a</span><span> </span><span>=</span><span> </span><span>123</span></div></div><div><div><div>7</div></div><div><span>var</span><span><span> </span><span>p</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>(</span><span>'张三'</span><span>, </span><span>18</span><span>, </span><span>'男'</span><span>)</span></div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'a'</span><span> </span><span>in</span><span><span> </span><span>p</span><span>) </span></span><span>//true</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>p</span><span>.</span></span><span>hasOwnProperty</span><span>(</span><span>'a'</span><span>)) </span><span>//false</span></div></div><div><div><div>10</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>p</span><span>.</span></span><span>__proto__</span><span>.</span><span>hasOwnProterty</span><span>(</span><span>'a'</span><span>)) </span><span>//true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>
<p><strong>注意：</strong></p>
<ul>
<li>
<p>原型对象也是对象，所以它也有原型，当我们使用一个对象的属性或方法时，会现在自身中寻找，自身中如果有，则直接使用，如果没有则去原型对象中寻找，如果原型对象中有，则使用，如果没有则去原型的原型中寻找，直到找到Object对象的原型，Object对象的原型没有原型，如果在Object原型中依然没有找到，则返回<code>undefined</code></p>
</li>
<li>
<p><strong>通过原型对象添加的属性和方法函数本身是没有的</strong>，这就是静态方法和实例方法的区别</p>
</li>
</ul>
</li>
<li>
<p><strong>总结</strong></p>
<ul>
<li>函数的<code>prototype</code>属性: 在定义函数时自动添加的, 默认值是一个空<code>Object</code>对象</li>
<li>对象的<code>__proto__</code>属性: 创建对象时自动添加的, 默认值为构造函数的<code>prototype</code>属性值</li>
<li>实例与构造函数原型有直接联系，但是实例与构造函数之间没有。</li>
<li>程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)</li>
</ul>
</li>
</ul></section><section><h3>原型链<a href="#原型链"><span>#</span></a></h3><ul>
<li>每个构造函数都有一个原型对象，原型有一个属性（<code>constructor</code>）指回构造函数，而实例有一个内部指针（<code>__proto__</code>）指向原型，又原型也是另一个类型的实例，那就意味着这个原型本身有一个内部指针指向另一个原型，相应的另一个原型也有一个指针指向另一个构造函数。这样实例和原型之间就构造了一条原型链。</li>
<li>任何函数的默认原型都是一个<code>Object</code>的实例，因此构造函数的原型对象都有<code>__proto__</code>指向<code>Object.prototype</code>（包括<code>Function</code>），而<code>Object.__proto__ === null</code>，因此Object.__proto__是整条原型链的末端。</li>
</ul><p><strong>注：</strong></p><ul>
<li><strong>实例对象的<code>__proto__</code>等于构造函数的prototype</strong></li>
<li>实例对象的<code>prototype</code>是<code>undefined</code></li>
<li>任何函数的构造函数都是<code>Function</code>，包括<code>Function</code>也不例外，因此<code>Function.__proto__ === Function.prototype</code></li>
<li><code>function</code>是<code>Function</code>的实例，实例化的对象是<code>Object</code>的实例</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>Person</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>name</span><span>: </span><span>'123'</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>Person</span><span>.</span><span>prototype</span><span>.</span><span>name</span><span> </span><span>=</span><span> </span><span>'aaa'</span></div></div><div><div><div>5</div></div><div><span>var</span><span><span> </span><span>p1</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>()</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>p1</span><span>.</span></span><span>name</span><span>) </span><span>//aaa</span></div></div><div><div><div>7</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>Function</span><span>.</span><span>prototype</span><span> </span><span>===</span><span><span> </span><span>Object</span><span>.</span></span><span>__proto__</span><span>) </span><span>//true (Object是Function的实例对象</span></div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Function</span><span>.</span></span><span>__proto__</span><span> </span><span>===</span><span> </span><span>Function</span><span>.</span><span>prototype</span><span>) </span><span>//true</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>Object</span><span>.</span><span>prototype</span><span>.</span><span>prototype</span><span>) </span><span>//undefined</span></div></div><div><div><div>10</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>Function</span><span>.</span><span>prototype</span><span>.</span><span>__proto__</span><span> </span><span>===</span><span> </span><span>Object</span><span>.</span><span>prototype</span><span>) </span><span>//true</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Person</span><span>.</span></span><span>__proto__</span><span> </span><span>===</span><span> </span><span>Function</span><span>.</span><span>prototype</span><span>) </span><span>//true</span></div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>Person</span><span>.</span><span>prototype</span><span>.</span><span>__proto__</span><span> </span><span>===</span><span> </span><span>Object</span><span>.</span><span>prototype</span><span>) </span><span>//true</span></div></div><div><div><div>13</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>Object</span><span>.</span></span><span>__proto__</span><span> </span><span>===</span><span> </span><span>Function</span><span>.</span><span>prototype</span><span>) </span><span>//true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="/images/javascript/%E5%8E%9F%E5%9E%8B%E9%93%BE1.png" alt="原型链1" /><figcaption>原型链1</figcaption></figure><p></p><ul>
<li>重写函数的prototype后，相当于prototype指向了一个新的对象</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>var</span><span> </span><span>Person</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>name</span><span>: </span><span>'123'</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span>  </span><span>Person</span><span>.</span><span>prototype</span><span>.</span><span>name</span><span> </span><span>=</span><span> </span><span>'aaa'</span></div></div><div><div><div>5</div></div><div><span>  </span><span>var</span><span><span> </span><span>p1</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>()</span></div></div><div><div><div>6</div></div><div><span>  </span><span>Person</span><span>.</span><span>prototype</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'bbb'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>      </span><span>age</span><span>:</span><span> </span><span>5</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>  </span><span>var</span><span><span> </span><span>p2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Person</span><span>()</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p1</span><span>.</span></span><span>constructor</span><span>) </span><span>//Person</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>Person</span><span>.</span></span><span>constructor</span><span>) </span><span>//Function</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p1</span><span>.</span></span><span>age</span><span>) </span><span>//undefined</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p1</span><span>.</span></span><span>name</span><span>) </span><span>//aaa</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p1</span><span>.</span></span><span>__proto__</span><span>.</span><span>constructor</span><span>.</span><span>prototype</span><span>.</span><span>age</span><span>) </span><span>//5</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p2</span><span>.</span></span><span>age</span><span>) </span><span>//5</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>p1</span><span>.</span></span><span>name</span><span>) </span><span>//bbb</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p></p><figure><img src="/images/javascript/%E5%8E%9F%E5%9E%8B%E9%93%BE2.png" alt="原型链2" /><figcaption>原型链2</figcaption></figure><p></p></section></section><section><h2>继承<a href="#继承"><span>#</span></a></h2><section><h3>通过原型链继承<a href="#通过原型链继承"><span>#</span></a></h3><ul>
<li>
<p>套路</p>
<ol>
<li>定义父类型构造函数</li>
<li>给父类型的原型添加方法</li>
<li>定义子类型的构造函数</li>
<li>创建父类型的对象赋值给子类型的原型</li>
<li>将子类型原型的构造属性设置为子类型</li>
<li>给子类型原型添加方法</li>
<li>创建子类型的对象: 可以调用父类型的方法</li>
</ol>
</li>
<li>
<p>关键</p>
<p>子类型的原型为父类型的一个实例对象</p>
<p>function SuperType() {
this.colors = [‘red’, ‘blue’, ‘green’]
this.name = 2
}</p>
<p>function SubType() {}</p>
<p>// 继承 SuperType
SubType.prototype = new SuperType()</p>
<p>let instance1 = new SubType()
instance1.name += 2
instance1.colors.push(‘black’)
console.log(instance1.colors) // “red,blue,green,black”
console.log(instance1.name) // 4</p>
<p>let instance2 = new SubType()
console.log(instance2.colors) // “red,blue,green,black”
console.log(instance2.name) // 2</p>
</li>
</ul><p>在这个例子中， <code>SuperType</code> 构造函数定义了一个 <code>colors</code> 属性，其中包含一个数组（引用值）。每个 <code>SuperType</code> 的实例都会有自己的 <code>colors</code> 属性，包含自己的数组。但是，当 <code>SubType</code> 通过原型继承SuperType 后， <code>SubType.prototype</code> 变成了 <code>SuperType</code> 的一个实例，因而也获得了自己的 <code>colors</code>属性。这类似于创建了 <code>SubType.prototype.colors</code> 属性。最终结果是， <code>SubType</code> 的所有实例都会共享这个 <code>colors</code> 属性。</p><p>导致<code>SubType</code>的每个实例引用 <code>colors</code> 属性, 都会从原型链中寻找<code>SubType.prototype.colors</code> 属性，相当于每个<code>SubType</code>都共用同一个 <code>colors</code> 属性。</p><p>而对于基本数据类型来说，执行instance1.name += 2 这行代码，会在instance1 实例中添加name属性，然后在从SubType.prototype中读取name属性的值（2），最后 +2，等价于instance1.name = SubType.prototype.name + 2。而SubType.prototype.name的值没有改变，仅读取instance2.name的值时，instance2会从原型链中读取SubType.prototype.name的值（2）</p></section><section><h3>盗用构造函数继承<a href="#盗用构造函数继承"><span>#</span></a></h3><ul>
<li>套路:
<ol>
<li>定义父类型构造函数</li>
<li>定义子类型构造函数</li>
<li><strong>在子类型构造函数中调用父类型构造</strong></li>
</ol>
</li>
<li>关键:
<ol>
<li>在子类型构造函数中通用call() 调用父类型构造函数</li>
</ol>
</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>SuperType</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>SubType</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>// 继承 SuperType 并传参</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>SuperType</span><span>.</span><span>call</span><span>(</span><span>this</span><span>, </span><span>'Nicholas'</span><span>) </span><span>// this.SuperType("Nicholas") 等价于 this.name = name</span></div></div><div><div><div>8</div></div><div><span>    </span><span>// 实例属性</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>age</span><span> </span><span>=</span><span> </span><span>29</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>let</span><span><span> </span><span>instance</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>SubType</span><span>()</span></div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>instance</span><span>.</span></span><span>name</span><span>) </span><span>// "Nicholas";</span></div></div><div><div><div>13</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>instance</span><span>.</span></span><span>age</span><span>) </span><span>// 29</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。
在 <code>SubType</code> 构造函数中调用 <code>SuperType</code> 构造函数时传入这个参数，实际上会在 <code>SubType</code> 的实例上定义 <code>name</code> 属性。</p></section><section><h3>组合继承<a href="#组合继承"><span>#</span></a></h3><ul>
<li>原型链+盗用构造函数的组合继承
<ul>
<li>使用原型链继承原型上的属性和方法</li>
<li>通过盗用构造函数继承实例属性</li>
</ul>
</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>SuperType</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>name</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>colors</span><span> </span><span>=</span><span> [</span><span>'red'</span><span>, </span><span>'blue'</span><span>, </span><span>'green'</span><span>]</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>SuperType</span><span>.</span><span>prototype</span><span>.</span><span>sayName</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>name</span><span>)</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>function</span><span> </span><span>SubType</span><span><span>(</span><span>name</span><span>, </span><span>age</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>// 继承属性</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>SuperType</span><span>.</span><span>call</span><span>(</span><span>this</span><span><span>, </span><span>name</span><span>)</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>age</span><span> </span><span>=</span><span><span> </span><span>age</span></span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div><span>// 继承方法</span></div></div><div><div><div>15</div></div><div><span>SubType</span><span>.</span><span>prototype</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>SuperType</span><span>()</span></div></div><div><div><div>16</div></div><div><span>SubType</span><span>.</span><span>prototype</span><span>.</span><span>sayAge</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>this</span><span>.</span><span>age</span><span>)</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>let</span><span><span> </span><span>instance1</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>SubType</span><span>(</span><span>'Nicholas'</span><span>, </span><span>29</span><span>)</span></div></div><div><div><div>21</div></div><div><span><span>instance1</span><span>.</span></span><span>colors</span><span>.</span><span>push</span><span>(</span><span>'black'</span><span>)</span></div></div><div><div><div>22</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>instance1</span><span>.</span></span><span>colors</span><span>) </span><span>// "red,blue,green,black"</span></div></div><div><div><div>23</div></div><div><span><span>instance1</span><span>.</span></span><span>sayName</span><span>() </span><span>// "Nicholas";</span></div></div><div><div><div>24</div></div><div><span><span>instance1</span><span>.</span></span><span>sayAge</span><span>() </span><span>// 29</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>let</span><span><span> </span><span>instance2</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>SubType</span><span>(</span><span>'Greg'</span><span>, </span><span>27</span><span>)</span></div></div><div><div><div>27</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>instance2</span><span>.</span></span><span>colors</span><span>) </span><span>// "red,blue,green"</span></div></div><div><div><div>28</div></div><div><span><span>instance2</span><span>.</span></span><span>sayName</span><span>() </span><span>// "Greg"</span></div></div><div><div><div>29</div></div><div><span><span>instance2</span><span>.</span></span><span>sayAge</span><span>() </span><span>// 27</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在这个例子中， <code>SuperType</code> 构造函数定义了两个属性， <code>name</code> 和 <code>colors</code>，而它的原型上也定义了一个方法叫 <code>sayName()</code>。
<code>SubType</code> 构造函数调用了 <code>SuperType</code> 构造函数，传入了 <code>name</code> 参数，然后又定义了自己的属性 <code>age</code>。此外， <code>SubType.prototype</code> 也被赋值为 <code>SuperType</code> 的实例。原型赋值之后，又在这个原型上添加了新方法 <code>sayAge()</code>。
这样，就可以创建两个 <code>SubType</code> 实例，让这两个实例都有自己的属性，包括 <code>colors</code>，同时还共享相同的方法。</p><p></p><figure><img src="/images/javascript/%E7%BB%84%E5%90%88%E7%BB%A7%E6%89%BF.png" alt="组合继承" /><figcaption>组合继承</figcaption></figure><p></p></section></section><section><h2>Date对象<a href="#date对象"><span>#</span></a></h2><ul>
<li>
<p>在JS中使用<code>Date</code>对象来表示一个时间</p>
</li>
<li>
<p>创建一个<code>Date</code>对象</p>
<ul>
<li>
<p>如果直接使用构造函数创建一个Date对象，则会封装为当前代码执行的时间</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date();  //封装此行代码执行的时间</span></div></div><div><div><div>2</div></div><div><span>console.log(date); //打印date  Fri Dec 11 2020 23:32:17 GMT+0800 (中国标准时间)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
<li>
<p>创建一个指定的时间对象</p>
<ul>
<li>
<p>需要在构造函数中传递一个表示时间的字符串作为参数</p>
</li>
<li>
<p>日期的格式: 月/日/年 时:分&lt;秒&gt;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date("2/18/2011 11:10:30");</span></div></div><div><div><div>2</div></div><div><span>console.log(date); //2011-02-18T03:10:30.000Z</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul><section><h3><code>getDate()</code><a href="#getdate"><span>#</span></a></h3><ul>
<li>
<p>获取当前日期对象是几日</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date("2/18/2011 11:10:30");</span></div></div><div><div><div>2</div></div><div><span>var d = date.getDate();</span></div></div><div><div><div>3</div></div><div><span>console.log(d); //18</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3><code>getDay()</code><a href="#getday"><span>#</span></a></h3><ul>
<li>
<p>获取当前日期对象时周几</p>
</li>
<li>
<p>会返回一个0-6的值
0 表示周日
1 表示周一</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date("2/18/2011 11:10:30")</span></div></div><div><div><div>2</div></div><div><span>var d = date.getDay()</span></div></div><div><div><div>3</div></div><div><span>console.log(d) //5 (周六)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3><code>getMonth()</code><a href="#getmonth"><span>#</span></a></h3><ul>
<li>
<p>获取当前时间对象的月份</p>
</li>
<li>
<p>会返回一个0-11的值
0 表示1月
1 表示2月
11 表示12月</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date("2/18/2011 11:10:30");</span></div></div><div><div><div>2</div></div><div><span>var d = date.getMonth();</span></div></div><div><div><div>3</div></div><div><span>console.log(d); //1</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>​</p></section><section><h3><code>getFullYear()</code><a href="#getfullyear"><span>#</span></a></h3><ul>
<li>
<p>获取当前日期对象的年份</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date("2/18/2011 11:10:30");</span></div></div><div><div><div>2</div></div><div><span>var d = date.getFullYear();</span></div></div><div><div><div>3</div></div><div><span>console.log(d); //2011</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3><code>getTime()</code><a href="#gettime"><span>#</span></a></h3><ul>
<li>获取当前日期对象的时间戳
<ul>
<li>时间戳，指的是从格林威治标准时间的1970年1月1日，0时0分0秒 到当前日期所花费的毫秒数</li>
</ul>
</li>
<li>计算机底层在保存时间时使用都是时间戳
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var date = new Date("2/18/2011 11:10:30")</span></div></div><div><div><div>2</div></div><div><span>var time = date.getTime()</span></div></div><div><div><div>3</div></div><div><span>console.log(time) //1297998630000 (毫秒数)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3><code>Date.now()</code><a href="#datenow"><span>#</span></a></h3><ul>
<li>
<p>获取当前的时间戳</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var time = Date.now();</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>可以利用时间戳来测试代码的执行的性能</p>
</li>
</ul></section></section><section><h2>Math对象<a href="#math对象"><span>#</span></a></h2><p><code>Math</code>和其他的对象不同，它不是一个构造函数，它属于一个工具类不用创建对象，它里边封装了数学运算相关的属性和方法</p><section><h3><code>abs()</code><a href="#abs"><span>#</span></a></h3><p>可以用来计算一个数的绝对值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>console.log(Math.abs(-2)) //2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>Math.ceil()</code><a href="#mathceil"><span>#</span></a></h3><p>可以对一个数进行向上取整，小数位只有有值就自动进1</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log(Math.ceil(1.1)) //2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>Math.floor()</code><a href="#mathfloor"><span>#</span></a></h3><p>可以对一个数进行向下取整，小数部分会被舍掉</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>console.log(Math.floor(1.99)) //1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>Math.round()</code><a href="#mathround"><span>#</span></a></h3><p>可以对一个数进行四舍五入取整</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>console.log(Math.round(1.4)) //1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>Math.random()</code><a href="#mathrandom"><span>#</span></a></h3><p>可以用来生成一个0-1之间的随机数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//生成一个0-10的随机数</span></div></div><div><div><div>2</div></div><div><span>Math.round(Math.random() * 10)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>//生成一个1-5之间的随机数</span></div></div><div><div><div>5</div></div><div><span>Math.round(Math.random() * 5 + 1)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>max()</code><a href="#max"><span>#</span></a></h3><p>可以获取多个数中的最大值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var max = Math.max(10,45,30,100) // 100</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>min()</code><a href="#min"><span>#</span></a></h3><p>可以获取多个数中的最小值</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var min = Math.min(10,45,30,100) // 10</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>Math.pow(x,y)</code><a href="#mathpowxy"><span>#</span></a></h3><p>返回x的y次幂</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>console.log(Math.pow(12,3)) //12^3</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3><code>Math.sqrt()</code><a href="#mathsqrt"><span>#</span></a></h3><p>用于对一个数进行开方运算</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>console.log(Math.sqrt(20))</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/han-shu/</id>
      <title type="text">函数</title>
      <published>2021-10-16T08:17:45.000Z</published>
      <updated>2021-10-16T08:17:45.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/han-shu/"/>
      <summary type="text">函数也是一个对象，也具有普通对象的功能 函数中可以封装一些代码，在需要的时候可以去调用函数来执行这些代码 使用typeof检查一个函数时会返回function</summary>
      <content type="html"><![CDATA[<section><h1>函数（Function）<a href="#函数function"><span>#</span></a></h1><ul>
<li>函数也是一个对象，也具有普通对象的功能</li>
<li>函数中可以封装一些代码，在需要的时候可以去调用函数来执行这些代码</li>
<li>使用typeof检查一个函数时会返回function</li>
</ul><section><h2>创建函数<a href="#创建函数"><span>#</span></a></h2><ul>
<li>
<p>函数声明
function 函数名([形参1,形参2…形参N]){
语句…
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function fun() {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>consloe.log("hellow");</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>函数表达式
var 函数名 = function([形参1,形参2…形参N]){
语句…
};</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var fun = function() {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>consloe.log("hellow");</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h2>调用函数<a href="#调用函数"><span>#</span></a></h2><ul>
<li>
<p>语法：
函数对象([实参1,实参2…实参N]);
fun(); sum(); alert(); Number(); parseInt();
//fun() 直接调用
//obj.fun() 通过对象调用
//new fun() new调用
//fun.call(obj) fun.apply(obj) 临时让fun成为obj的方法进行调用 obj.fun()
var obj ={};
funcation fun() {
this.name = “123”;
}
//obj.fun() 错误，obj中没有fun这个方法
fun.call(obj)
console.log(obj.name) // 123</p>
</li>
<li>
<p>当我们调用函数时，函数中封装的代码会按照编写的顺序执行</p>
</li>
</ul></section><section><h2>形参和实参<a href="#形参和实参"><span>#</span></a></h2><ul>
<li>形参：形式参数
<ul>
<li>定义函数时，可以在()中定义一个或多个形参，形参之间使用 , 隔开</li>
<li>定义形参就相当于在函数内声明了对应的变量但是并不赋值，</li>
<li>形参会在调用时才赋值。</li>
</ul>
</li>
<li>实参：实际参数
<ul>
<li>调用函数时，可以在()传递实参，传递的实参会赋值给对应的形参,</li>
<li>调用函数时JS解析器不会检查实参的类型和个数，可以传递任意数据类型的值。</li>
<li>如果实参的数量大于形参，多余实参将不会赋值，</li>
<li>如果实参的数量小于形参，则没有对应实参的形参将会赋值undefined</li>
</ul>
</li>
</ul></section><section><h2>返回值 return<a href="#返回值-return"><span>#</span></a></h2><p>可以使用 return 来设置函数的返回值
语法：
return 值;</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>return a;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>return后的值将会会作为函数的执行结果返回，可以定义一个变量，来接收该结果</li>
<li>在函数中return后的语句都不会执行</li>
<li>如果return语句后不跟任何值就相当于返回一个undefined， 如果函数中不写return，则也会返回undefined</li>
</ul></section><section><h2>立即执行函数(IIFE)<a href="#立即执行函数iife"><span>#</span></a></h2><ul>
<li>
<p>函数定义完，立即被调用，这种函数叫做立即执行函数，匿名函数直接调用</p>
</li>
<li>
<p>立即执行函数往往只会执行一次</p>
</li>
<li>
<p>作用：隐藏实现，不会污染外部命名空间，编写js模块
语法：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>(function([形参1,形参2...形参N]){</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>语句...</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>})([实参1,实参2...实参N]);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>(function(){</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>consloe.log("hellow");</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>})();</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>(function(a,b){</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>console.log("a = "+a);</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>console.log("b = "+b);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>})(123,456);</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h2>函数的声明提前<a href="#函数的声明提前"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 使用函数声明形式创建的函数 function 函数(){}</span></div></div><div><div><div>2</div></div><div><span>- 它会在所有的代码执行之前就被创建，所以我们可以在函数声明前来调用函数</span></div></div><div><div><div>3</div></div><div><span>- 使用函数表达式创建的函数，不会被声明提前，所以不能在声明前调用</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>作用域<a href="#作用域"><span>#</span></a></h2><ul>
<li>作用域指一个变量的作用的范围</li>
</ul><section><h3>全局作用域<a href="#全局作用域"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 直接编写在script标签中的JS代码，都在全局作用域</span></div></div><div><div><div>2</div></div><div><span>- 全局作用域在页面打开时创建，在页面关闭时销毁</span></div></div><div><div><div>3</div></div><div><span>- 在全局作用域中有一个全局对象window，它代表的是一个浏览器的窗口，它由浏览器创建我们可以直接使用</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>- 在全局作用域中：</span></div></div><div><div><div>6</div></div><div><span>创建的变量都会作为window对象的属性保存</span></div></div><div><div><div>7</div></div><div><span>创建的函数都会作为window对象的方法保存</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>- 全局作用域中的变量都是全局变量，在页面的任意的部分都可以访问的到</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>函数作用域<a href="#函数作用域"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 函数作用域是在function字样里的作用域,调用函数时创建函数作用域，函数执行完毕以后，函数作用域销毁</span></div></div><div><div><div>2</div></div><div><span>- 每调用一次函数就会创建一个新的函数作用域，它们之间互相独立的</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>- 当在函数作用域操作一个变量时，它会先在自身作用域中寻找，如果没有，再在离得最近的一级作用域，直到找到全局变量，如果全局作用域中没有找到，就会报错</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>- 如果想直接在函数作用域中用全局的变量,使用window.a来调用(前提是用var或者直接写来声明的变量)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>- 在函数作用域中也有声明提前的特性，使用变量声明关键字声明的变量，会在函数中所有的代码执行之前被声明，这个变量是在函数作用域中被声明的，同理在函数中声明的函数也是一样</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>- 有参的函数在调用的时候不传入实参,形参的值是undefined，因为在定义形参的时候相当于在函数内部用var声明了一个变量，没有赋值的变量一律为undefined，即使全局变量中有一样的变量也会用内部没有赋值的变量</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>关键字 this<a href="#关键字-this"><span>#</span></a></h2><ul>
<li>解析器在调用函数每次都会向函数内部传递进一个隐含的参数这个隐含的参数就是this</li>
<li>this指向的是一个对象，这个对象我们称为函数执行的上下文对象
<ul>
<li>根据函数的调用方式的不同，this会指向不同的对象
<ol>
<li>
<p>以函数的形式调用时，this永远都是window
var name = “abc”;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>function fun(){</span></div></div><div><div><div>2</div></div><div><span><span>   </span></span><span>console.log(this.name);//此时this.name指向全局作用域的window.name</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span> </span></span><span>fun();</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>以方法的形式调用时，this就是调用方法的那个对象
function fun(){
console.log(this.name);//此时的this.name指向的是定义的obj对象中的name属性
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>var obj = {</span></div></div><div><div><div>2</div></div><div><span><span>       </span></span><span>name:"abc",</span></div></div><div><div><div>3</div></div><div><span><span>       </span></span><span>sayName:fun</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>};</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span> </span></span><span>obj.sayname();</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>以构造函数的形式调用时，this就是新创建的那个对象
function Person(name , age , gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
console.log(this.name); //this指向的都是Person类中的属性
};
}</p>
</li>
</ol>
</li>
</ul>
</li>
</ul></section><section><h2>构造函数<a href="#构造函数"><span>#</span></a></h2><ul>
<li>构造函数就是一个普通的函数，创建方式和普通函数没有区别,
<ul>
<li>
<p>不同的是构造函数习惯上首字母大写</p>
</li>
<li>
<p>构造函数和普通函数的区别就是调用方式的不同
普通函数是直接调用，而构造函数需要使用new关键字来调用</p>
</li>
<li>
<p>构造函数的执行流程：
1.立刻创建一个新的对象
2.将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
3.逐行执行函数中的代码
4.将新建的对象作为返回值返回</p>
</li>
<li>
<p>使用同一个构造函数创建的对象，我们称为一类对象，也将一个构造函数称为一个类。
我们将通过一个构造函数创建的对象，称为是该类的实例</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function Person(name , age , gender){</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>this.name = name;</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>this.age = age;</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>this.gender = gender;</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>this.sayName = function(){</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>console.log(this.name);</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>};</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>var p1 = new Person("张三", 18, "男"); //此时的person是以构造函数的形式赋值给p1变量</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>var p2 = Person("李四", 18, "男"); //此时的person是以普通函数的形式赋值给p1变量</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul></section><section><h2>闭包<a href="#闭包"><span>#</span></a></h2><ul>
<li>
<p>当函数可以记住并访问所在的词法作用域时， 就产生了闭包， 即使函数是在当前词法作用域之外执行。
通俗来说当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
（函数嵌套，内部函数引用外部函数的变量）
function foo() {
var a = 2;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>      </span></span><span>function bar() {</span></div></div><div><div><div>2</div></div><div><span><span>          </span></span><span>console.log(a);</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>return bar;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>var baz = foo();</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>baz(); //2</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<ul>
<li>在上述代码中，函数 bar() 的词法作用域能够访问 foo() 的内部作用域（foo()内部嵌套了bar()函数）。将 bar() 函数本身当作一个值类型进行传递，使得bar()能够在它的词法作用域外部进行调用</li>
<li>我们都知道在函数执行完毕后就，将由垃圾回收机制释放掉foo()函数所占用的内存，但是 var baz = foo() 这行代码中，bar()函数一直处于执行状态，而foo()函数内部作用域任然在被bar()函数使用（bar()打印变量a的值），拜 bar() 所声明的位置所赐， 它拥有涵盖 foo() 内部作用域的闭包， 使得该作用域能够一直存活， 以供 bar() 在之后任何时间进行引用。</li>
<li>bar() 依然持有对该作用域的引用， 而这个引用就叫作闭包。</li>
</ul>
</li>
<li>
<p>循环与闭包
正常情况下，我们对这段代码行为的预期是分别输出数字 1~5，每秒一次， 每次一个。但实际上， 这段代码在运行时会以每秒一次的频率输出五次 6。
for (var i = 1; i &lt;= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
因为延迟回调函数的执行总是在循环执行结束才执行的，此时执行回调函数是 i === 6。尽管循环中5个函数都是分别定义的，但是它们都处在一个<strong>共享全局作用域</strong>中，因此实际上只有一个 i 。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>for (var i = 1; i &lt;= 5; i++) {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>(function (i) {</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>setTimeout(function timer() {</span></div></div><div><div><div>4</div></div><div><span><span>              </span></span><span>console.log(i);</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>}, j * 1000);</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>})(i);</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>IIFE 会通过声明并立即执行一个函数来创建作用域。在迭代内使用 IIFE 会为每个迭代都生成一个新的作用域， 使得延迟函数的回调可以将新的作用域封闭在每个迭代内部， 每个迭代中都会含有一个具有正确值的变量供我们访问。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>for (let i = 1; i &lt;= 5; i++) {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>setTimeout(function timer() {</span></div></div><div><div><div>3</div></div><div><span><span>          </span></span><span>console.log(i);</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>}, i * 1000);</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>还能使用let声明创建块作用域，并且for 循环头部的 let 声明还会有一个特殊的行为。 这个行为指出变量在循环过程中不止被声明一次， 每次迭代都会声明。 随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。</p>
</li>
<li>
<p><strong>模块</strong></p>
<ul>
<li>
<p>我们可以利用闭包创建JS模块</p>
<ul>
<li>
<p>具有特定功能的js文件</p>
</li>
<li>
<p>将所有的数据和功能都封装在一个函数内部(私有的)</p>
</li>
<li>
<p>只向外暴露一个包信n个方法的对象或函数</p>
</li>
<li>
<p>模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>function CoolModule() {</span></div></div><div><div><div>2</div></div><div><span><span>      </span></span><span>var something = "cool";</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>var another = [1, 2, 3];</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>function doSomething() {</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>console.log(something);</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>function doAnother() {</span></div></div><div><div><div>10</div></div><div><span><span>          </span></span><span>console.log(another.join(" ! "));</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>return {</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>doSomething: doSomething,</span></div></div><div><div><div>14</div></div><div><span><span>          </span></span><span>doAnother: doAnother</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>};</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>//将以上部分通过js文件保存起来，直接引用外部js文件</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>var foo = CoolModule();</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>foo.doSomething(); // cool</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>foo.doAnother(); // 1 ! 2 ! 3</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul>
</li>
<li>
<p>首先， CoolModule() 只是一个函数， 必须要通过调用它来创建一个模块实例。 如果不执行外部函数， 内部作用域和闭包都无法被创建。</p>
</li>
<li>
<p>其次， CoolModule() 返回一个用对象字面量语法 { key: value, … } 来表示的对象。 这个返回的对象中含有对内部函数而不是内部数据变量的引用。 我们保持内部数据变量是隐藏且私有的状态。 可以将这个对象类型的返回值看作本质上是模块的公共 API。</p>
</li>
<li>
<p>这个对象类型的返回值最终被赋值给外部的变量 foo， 然后就可以通过它来访问 API 中的属性方法， 比如 foo.doSomething()。</p>
</li>
<li>
<p>doSomething() 和 doAnother() 函数具有涵盖模块实例内部作用域的闭包（ 通过调用CoolModule() 实现）。</p>
</li>
<li>
<p>模块模式需要具备两个必要条件。</p>
<ol>
<li>必须有外部的封闭函数， 该函数必须至少被调用一次（每次调用都会创建一个新的模块实例）。</li>
<li>封闭函数必须返回至少一个内部函数， 这样内部函数才能在私有作用域中形成闭包，并且可以访问或者修改私有的状态。</li>
</ol>
</li>
</ul>
</li>
<li>
<p>记住闭包产生的条件：<strong>当函数可以记住并访问所在的词法作用域， 即使函数是在当前词法作用域之外执行， 这时就产生了闭包。</strong></p>
</li>
</ul></section><section><h2>垃圾回收（GC）<a href="#垃圾回收gc"><span>#</span></a></h2><ul>
<li>
<p>就像人生活的时间长了会产生垃圾一样，程序运行过程中也会产生垃圾，这些垃圾积攒过多以后，会导致程序运行的速度过慢，所以我们需要一个垃圾回收的机制，来处理程序运行过程中产生垃圾</p>
</li>
<li>
<p>当一个对象没有任何的变量或属性对它进行引用，此时我们将永远无法操作该对象，此时这种对象就是一个垃圾，这种对象过多会占用大量的内存空间，导致程序运行变慢，所以这种垃圾必须进行清理。</p>
</li>
<li>
<p>在JS中拥有自动的垃圾回收机制，会自动将这些垃圾对象从内存中销毁，我们不需要也不能进行垃圾回收的操作</p>
</li>
<li>
<p>我们需要做的只是要将不再使用的对象设置null即可
var obj = new Object();</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>//对对象进行各种操作。。。。</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span> </span></span><span>obj = null;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/shi-jian/</id>
      <title type="text">事件</title>
      <published>2021-10-16T06:51:28.000Z</published>
      <updated>2021-10-16T06:51:28.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/shi-jian/"/>
      <summary type="text">HTML 事件是发生在 HTML 元素上的事情。 当在 HTML 页面中使用 JavaScript 时， JavaScript 可以触发这些事件。</summary>
      <content type="html"><![CDATA[<p>HTML 事件是发生在 HTML 元素上的事情。</p>
<p>当在 HTML 页面中使用 JavaScript 时， JavaScript 可以触发这些事件。</p>
<section><h2>事件的冒泡<a href="#事件的冒泡"><span>#</span></a></h2><p>事件的冒泡（Bubble）</p><ul>
<li>所谓的冒泡指的就是事件的向上传导，当后代元素上的事件被触发时，其祖先元素的相同事件也会被触发</li>
<li>在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡
<ul>
<li>可以将事件对象的 cancelBubble 设置为true，即可取消冒泡，这个需要设置在子元素的事件中，并且大多数都是设置的可以冒泡，除了某些函数默认不冒泡
event.cancelBubble = true;</li>
</ul>
</li>
</ul><p>如果在父元素里面设置了一个事件,而在子元素中也有相同的事件,不想要父元素的事件在子元素的范围内出现时，可以通过多种方式设置取消冒泡</p><ol>
<li>event.cancelBubble = true</li>
<li>event.stopPropagation();</li>
</ol></section>
<section><h2>事件的委派<a href="#事件的委派"><span>#</span></a></h2><ul>
<li>指将事件统一绑定给元素的共同的祖先元素，这样当后代元素上的事件触发时，会一直冒泡到祖先元素，从而通过祖先元素的响应函数来处理事件。</li>
<li>事件委派是利用了冒泡，通过委派可以减少事件绑定的次数，提高程序的性能</li>
</ul><p>例：
为每一个超链接都绑定一个单击响应函数，这里我们为每一个超链接都绑定了一个单击响应函数，这种中操作比较麻烦，而且这些操作只能为已有的超链接设置事件，而新添加超链接必须重新绑定</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//获取所有的a</span></div></div><div><div><div>2</div></div><div><span>var allA = document.getElementsByTagName("a");</span></div></div><div><div><div>3</div></div><div><span>//遍历</span></div></div><div><div><div>4</div></div><div><span>for(var i=0 ; i&lt;allA.length ; i++){</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>allA[i].onclick = function(){</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>alert("我是a的单击响应函数！");</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们希望，只绑定一次事件，即可应用到多个元素上，即使元素是后添加的，我们可以尝试将其绑定给元素的共同的祖先元素</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//假设所以的a元素都是由一个父元素包裹，获取a的父元素</span></div></div><div><div><div>2</div></div><div><span>var parent=document.getElementById("parent");</span></div></div><div><div><div>3</div></div><div><span>//为a的父元素绑定事件</span></div></div><div><div><div>4</div></div><div><span>parent.onclick=function(){</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>alert("我是a的单击响应函数！");</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>12345但是这个例子需要判断如果触发的对象是我们期望的元素,就执行该代码,否则不执行,因为如果点击的是a元素之外的其它地方就不触发弹窗效果,这个时候需要使用target 事件对象中的event中的target表示的触发事件的对象</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>parent.onclick=function(event){</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>event=event||window.event;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>if(event.target.nodeName==="A"){  //nodeName返回的标签名全为大写</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>alert("我是a的单击响应函数！");</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>事件的绑定<a href="#事件的绑定"><span>#</span></a></h2><section><h3>绑定事件<a href="#绑定事件"><span>#</span></a></h3><ul>
<li>
<p>使用 对象.事件 = 函数 的形式绑定响应函数，它只能同时为一个元素的一个事件绑定一个响应函数，不能绑定多个，如果绑定了多个，则后边会覆盖掉前边的</p>
</li>
<li>
<p>使用addEventListener() （不支持IE8）可以同时为一个元素的相同事件同时绑定多个响应函数，这样当事件被触发时，响应函数将会按照函数的绑定顺序执行</p>
<ul>
<li>
<p>参数：
1.事件的字符串，不要on （onclick -&gt; click）
2.回调函数，当事件触发时该函数会被调用
3.是否在捕获阶段触发事件，需要一个布尔值，true表示事件在捕获阶段执行，flase表示在冒泡阶段执行，默认值为false(可选)，如果想要这两个事件都可以在同一个对象上发生必须添加两次事件</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>btn.addEventListener("click",function(){</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>alert(1);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>},false);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>btn.addEventListener("click",function(){</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>alert(2);</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>},false);</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/bian-liang-yu-zuo-yong-yu/</id>
      <title type="text">变量与作用域</title>
      <published>2021-10-16T06:49:28.000Z</published>
      <updated>2021-10-16T06:49:28.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/bian-liang-yu-zuo-yong-yu/"/>
      <summary type="text">字面量 字面量（常量），都是一些不可改变的值 比如 ：1 2 3 4 5 字面量都是可以直接使用，但是我们一般都不会直接使用字面量 变量 变量可以用来保存字面量，而且变量的值是可以任意改变的 变量更加方便我们使用，所以在开发中都是通过变量去保存一个字面量，而很少直接使用字面量，可以通过变量对字面量进…</summary>
      <content type="html"><![CDATA[<section><h1>变量<a href="#变量"><span>#</span></a></h1><ul>
<li><strong>字面量</strong><br />
字面量（常量），都是一些不可改变的值<br />
比如 ：1 2 3 4 5<br />
字面量都是可以直接使用，但是我们一般都不会直接使用字面量</li>
<li><strong>变量</strong><br />
变量可以用来保存字面量，而且变量的值是可以任意改变的<br />
变量更加方便我们使用，所以在开发中都是通过变量去保存一个字面量，而很少直接使用字面量，可以通过变量对字面量进行描述</li>
</ul><section><h2>传递参数<a href="#传递参数"><span>#</span></a></h2><ul>
<li>所有函数的参数都是按值传递的，在按值传递参数时，值会被复制到一个局部变量中（也就是arguments对象中的一个槽位）
function setName(obj) {
obj.name = “aaa”
obj = new Object()
obj.name = “bbb”
}
var person = new Object()
setName(person)
console.log(person.name) // aaa
如果是按照引用传递的话，person将会指向name为”bbb”的对象，而不是任然指向”aaa”的对象。
当obj在函数内部重写时，它变成了一个指向本地对象的指针，而那个本地对象在函数执行结束时就被销毁了。</li>
</ul></section></section>
<section><h1>执行上下文与作用域<a href="#执行上下文与作用域"><span>#</span></a></h1><section><h2>变量提升与函数提升<a href="#变量提升与函数提升"><span>#</span></a></h2><ul>
<li>
<p>变量声明提升：通过var定义(声明)的变量, 在定义语句之前就可以访问到，但值: undefined</p>
</li>
<li>
<p>函数声明提升：通过function声明的函数, 在之前就可以直接调用，但 值: 函数定义(对象)</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>//变量提升</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(a1) //可以访问, 但值是undefined</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>var a1 = 3</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>//函数提升</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>a2() // 可以直接调用，输出  a2()</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>function a2() {</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>console.log('a2()')</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>a3() //报错</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>var a3 = function(){</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>console.log('a3')</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>遇到函数表达式 var a3 = function(){}时，首先会将var a3上升到函数体顶部，然而此时的 a3 的值为 undefined,所以执行a3()报错。</p><ul>
<li><strong>注</strong>：
<ul>
<li>
<p>当遇到函数和变量同名且都会被提升的情况，函数声明优先级比较高（<strong>实际上是变量先提升到全局作用域中，但值为undefined，然后函数提升并且给函数直接赋值</strong>），因此变量声明会被函数声明所覆盖，但是可以重新赋值。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log(a) //输出：function a(){ console.log('函数') }</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>function a() {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>console.log("函数")</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>} //</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>var a = "变量"</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>console.log(a) //输出：'变量'</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul><p>function 声明的优先级比 var 声明高，也就意味着当两个同名变量同时被 function 和 var 声明时，function 声明会覆盖 var 声明。</p><p>这代码等效于：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>function a() {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console.log("函数");</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>var a</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>console.log(a) //输出：function a(){ console.log('函数') }</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>a = "变量" //赋值</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>console.log(a) //输出：'变量'</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>执行上下文<a href="#执行上下文"><span>#</span></a></h2><section><h3>全局执行上下文<a href="#全局执行上下文"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>* 在执行全局代码前将window确定为全局执行上下文</span></div></div><div><div><div>2</div></div><div><span>* 对全局数据进行预处理</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>* var定义的全局变量==&gt;undefined, 添加为window的属性</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>* function声明的全局函数==&gt;赋值(fun), 添加为window的方法</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>* this==&gt;赋值(window)</span></div></div><div><div><div>6</div></div><div><span>* 开始执行全局代码</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>函数执行上下文<a href="#函数执行上下文"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>* 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象</span></div></div><div><div><div>2</div></div><div><span>* 对局部数据进行预处理</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>* 形参变量==&gt;赋值(实参)==&gt;添加为执行上下文的属性</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>* arguments==&gt;赋值(实参列表), 添加为执行上下文的属性</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>* var定义的局部变量==&gt;undefined, 添加为执行上下文的属性</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>* function声明的函数 ==&gt;赋值(fun), 添加为执行上下文的方法</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>* this==&gt;赋值(调用函数的对象)</span></div></div><div><div><div>8</div></div><div><span>* 开始执行函数体代码</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>执行上下文栈<a href="#执行上下文栈"><span>#</span></a></h2><p>JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构，遵循后进先出的原则。</p><ul>
<li>JavaScript 执行在单线程上，所有的代码都是排队执行，遵循后进先出的原则。</li>
<li>一开始浏览器执行全局的代码时，首先创建全局的执行上下文，压入执行栈的顶部。</li>
<li>每当进入一个函数的执行就会创建函数的执行上下文，并且把它压入执行栈的顶部。当前函数执行完成后，当前函数的执行上下文出栈，并等待垃圾回收。</li>
<li>浏览器的 JS 执行引擎总是访问栈顶的执行上下文。</li>
<li>全局上下文只有唯一的一个，它在浏览器关闭时出栈。</li>
<li>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var color = "blue"</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>function changeColor() {</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>var anotherColor = "red"</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>function swapColors() {</span></div></div><div><div><div>5</div></div><div><span><span>          </span></span><span>var tempColor = anotherColor</span></div></div><div><div><div>6</div></div><div><span><span>          </span></span><span>anotherColor = color</span></div></div><div><div><div>7</div></div><div><span><span>          </span></span><span>color = tempColor</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>swapColors()</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>changeColor()</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<ul>
<li>当上述代码在浏览器中加载时，JavaScript 引擎会创建一个全局执行上下文并且将它推入当前的执行栈</li>
<li>调用 changeColor 函数时，此时 changeColor 函数内部代码还未执行，js 执行引擎立即创建一个 changeColor 的执行上下文，然后把这执行上下文压入到执行栈中。</li>
<li>执行 changeColor 函数过程中，调用 swapColors 函数，同样地，swapColors 函数执行之前也创建了一个 swapColors 的执行上下文，并压入到执行栈中。</li>
<li>swapColors 函数执行完成，swapColors 函数的执行上下文出栈，并且被销毁。</li>
<li>changeColor 函数执行完成，changeColor 函数的执行上下文出栈，并且被销毁。</li>
</ul>
</li>
</ul></section><section><h2>作用域<a href="#作用域"><span>#</span></a></h2><ul>
<li>我们可以这样理解：作用域就是一个独立的地盘，让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量，不同作用域下同名变量不会有冲突。</li>
<li>在创建函数会创建函数作用域，函数作用域的含义是指，属于这个函数的全部变量都可以在整个函数的范围内使用及复用（事实上在嵌套的作用域中也可以使用）。<strong>而外部作用域无法使用内部作用域的变量</strong></li>
<li>在ES6中引入了let关键字，用来在任意块中声明变量，在块中的变量无法在块作用域在进行引用
var foo = true;
if (foo) {
{ // &lt;— 显式的快
let bar = foo * 2;
bar = something( bar );
console.log( bar );
}
}
console.log( bar ); // ReferenceError</li>
</ul></section><section><h2>作用域链<a href="#作用域链"><span>#</span></a></h2><ul>
<li>
<p>作用域链是由多个上下级关系的作用域形成的链，他的方向是从下向上的（从内向外），查找变量时就是沿着作用域链来查找的
var a = 2;
function fn1() {
var b = 3;
function fn2() {
var c = 4;
console.log(c);
console.log(b);
console.log(a);
console.log(d);
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>fn2();</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>fn1(); //4, 3, 2, ReferenceError</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面例子中的作用域链就是fn2函数作用域—&gt;fn1函数作用域—&gt;全局作用域</p>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/shi-jian-dui-xiang/</id>
      <title type="text">事件对象</title>
      <published>2021-04-28T05:47:26.000Z</published>
      <updated>2021-04-28T05:47:26.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/shi-jian-dui-xiang/"/>
      <summary type="text">当事件的响应函数被触发时，浏览器每次都会将一个事件对象作为实参传递进响应函数,在事件对象中封装了当前事件相关的一切信息，比如：鼠标的坐标，键盘哪个按键被按下，鼠标滚轮滚动的方向...</summary>
      <content type="html"><![CDATA[<section><h2>事件对象<a href="#事件对象"><span>#</span></a></h2><ul>
<li>
<p>当事件的响应函数被触发时，浏览器每次都会将一个事件对象作为实参传递进响应函数,在事件对象中封装了当前事件相关的一切信息，比如：鼠标的坐标，键盘哪个按键被按下，鼠标滚轮滚动的方向…</p>
</li>
<li>
<p>注:</p>
<ul>
<li>
<p>一般都会把形参写在事件对象的形参里，虽然不写也不会报错，但是获取方式会相对麻烦</p>
</li>
<li>
<p>在IE8中，响应函数被触发时，浏览器不会传递事件对象。在IE8及以下的浏览器中，是将事件对象作为window对象的属性保存的
//写实参调用event
function eventTest(event){
event = event || window.event;
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>//不写实参调用event</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>function eventTest(){</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>var event = window.event || arguments[0];</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>//传入额外实参</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>function eventTest(a,b){</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>var event = window.event || arguments.callee.caller.arguments[0];</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>//如果传入了参数却如第二种写法的话，则arguments中将会传入已经传入的参数，</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>//这时获取的arguments[0]就会是第一个传入的参数</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul><section><h3>基本事件对象<a href="#基本事件对象"><span>#</span></a></h3><ul>
<li>静态变量</li>
</ul>

<table><thead><tr><th>静态变量</th><th>描述</th></tr></thead><tbody><tr><td>CAPTURING-PHASE</td><td>当前事件阶段为捕获阶段(1)</td></tr><tr><td>AT-TARGET</td><td>当前事件是目标阶段,在评估目标事件(1)</td></tr><tr><td>BUBBLING-PHASE</td><td>当前的事件为冒泡阶段 (3)</td></tr></tbody></table><ul>
<li>属性</li>
</ul>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>bubbles</td><td>返回布尔值，指示事件是否是起泡事件类型</td></tr><tr><td>cancelable</td><td>返回布尔值，指示事件是否可拥可取消的默认动作</td></tr><tr><td>currentTarget</td><td>返回其事件监听器触发该事件的元素</td></tr><tr><td>eventPhase</td><td>返回事件传播的当前阶段</td></tr><tr><td>target</td><td>返回触发此事件的元素（事件的目标节点）不兼容低版本IE，低版本IE用srcElement</td></tr><tr><td>timeStamp</td><td>返回事件生成的日期和时间</td></tr><tr><td>type</td><td>返回当前 Event 对象表示的事件的名称</td></tr></tbody></table><ul>
<li>方法</li>
</ul>

<table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>initEvent()</td><td>初始化新创建的 Event 对象的属性</td></tr><tr><td>preventDefault()</td><td>通知浏览器不要执行与事件关联的默认动作</td></tr><tr><td>stopPropagation()</td><td>不再派发事件</td></tr></tbody></table></section><section><h3>目标事件对象<a href="#目标事件对象"><span>#</span></a></h3>

<table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>addEventListener()</td><td>允许在目标事件中注册监听事件(IE8 = attachEvent())</td></tr><tr><td>dispatchEvent()</td><td>允许发送事件到监听器上 (IE8 = fireEvent())</td></tr><tr><td>removeEventListener()</td><td>运行一次注册在事件目标上的监听事件(IE8 = detachEvent())</td></tr></tbody></table></section><section><h3>事件监听对象<a href="#事件监听对象"><span>#</span></a></h3>

<table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>handleEvent()</td><td>把任意对象注册为事件处理程序</td></tr></tbody></table></section><section><h3>文档事件对象<a href="#文档事件对象"><span>#</span></a></h3>

<table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>createEvent()</td><td>返回新创建的event对象</td></tr></tbody></table></section><section><h3>鼠标/键盘事件对象<a href="#鼠标键盘事件对象"><span>#</span></a></h3><ul>
<li>属性</li>
</ul>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>altKey</td><td>返回当事件被触发时，“ALT” 是否被按下</td></tr><tr><td>button</td><td>返回当事件被触发时，哪个鼠标按钮被点击</td></tr><tr><td>clientX</td><td>返回当事件被触发时，鼠标指针的水平坐标</td></tr><tr><td>clientY</td><td>返回当事件被触发时，鼠标指针的垂直坐标</td></tr><tr><td>ctrlKey</td><td>返回当事件被触发时，“CTRL” 键是否被按下</td></tr><tr><td>Location</td><td>返回按键在设备上的位置</td></tr><tr><td>charCode</td><td>返回onkeypress事件触发键值的字母代码</td></tr><tr><td>key</td><td>在按下按键时返回按键的标识符</td></tr><tr><td>keyCode</td><td>返回onkeypress事件触发的键的值的字符代码，或者 onkeydown 或 onkeyup 事件的键的代码</td></tr><tr><td>which</td><td>返回onkeypress事件触发的键的值的字符代码，或者 onkeydown 或 onkeyup 事件的键的代码</td></tr><tr><td>metaKey</td><td>返回当事件被触发时，“meta” 键是否被按下</td></tr><tr><td>relatedTarget</td><td>返回与事件的目标节点相关的节点</td></tr><tr><td>screenX</td><td>返回当某个事件被触发时，鼠标指针的水平坐标</td></tr><tr><td>screenY</td><td>返回当某个事件被触发时，鼠标指针的垂直坐标</td></tr><tr><td>shiftKey</td><td>返回当事件被触发时，“SHIFT” 键是否被按下</td></tr></tbody></table><ul>
<li>方法</li>
</ul>

<table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>initMouseEvent()</td><td>初始化鼠标事件对象的值</td></tr><tr><td>initKeyboardEvent()</td><td>初始化键盘事件对象的值</td></tr></tbody></table></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/dom-jie-shao/</id>
      <title type="text">DOM介绍</title>
      <published>2021-04-28T05:45:31.000Z</published>
      <updated>2021-04-28T05:45:31.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/dom-jie-shao/"/>
      <summary type="text">DOM全称Document Object Model(文档对象模型) 文档:整个HTML网页文档对象 对象:网页中的每一个部分都被转换为了一个对象 模型:使用模型表示对象之间的关系，方便我们获取对象</summary>
      <content type="html"><![CDATA[<section><h2>DOM<a href="#dom"><span>#</span></a></h2><ul>
<li>DOM全称Document Object Model(文档对象模型)
<ul>
<li>文档&lt;整个html网页文档对象&gt;</li>
<li>对象&lt;网页中的每一个部分都被转换为了一个对象&gt;</li>
<li>模型&lt;使用模型表示对象之间的关系&gt;，方便我们获取对象</li>
</ul>
</li>
</ul><hr /><section><h3>节点<a href="#节点"><span>#</span></a></h3><ul>
<li><strong>Node(节点)是构成HTML5的基本单元</strong>，DOM节点主要分为四类
<ul>
<li><strong>文档节点</strong>&lt;整个html文档&gt;</li>
<li><strong>元素节点</strong></li>
<li><strong>属性节点</strong>&lt;元素的属性&gt;</li>
<li><strong>文本节点</strong></li>
</ul>
</li>
</ul>

<table><thead><tr><th>节点</th><th>nodeName(节点名)</th><th>nodeType(节点类型)</th><th>nodeValue(节点值)</th></tr></thead><tbody><tr><td>文档节点</td><td>#document</td><td>9</td><td>null</td></tr><tr><td>元素节点</td><td>标签名</td><td>1</td><td>null</td></tr><tr><td>属性节点</td><td>属性名</td><td>2</td><td>属性值</td></tr><tr><td>文本节点</td><td>#text</td><td>3</td><td>文本内容</td></tr></tbody></table><ul>
<li>浏览器已经为我们提供了文档节点对象window,该对象可以在页面中直接使用,代表的是整个网页</li>
<li>通过nodeType===1可以判断某节点是否是元素节点</li>
<li>文本节点包含了在写代码时的回车换行符间产生的空白，但是IE8及以下的浏览器中不会将空白节点当作子节点，而其他浏览器会</li>
<li>通过nodeName返回的标签名是全大写的</li>
<li>元素节点的名字也可以通过专门的TagName属性获得</li>
</ul><hr /></section><section><h3>DOM事件<a href="#dom事件"><span>#</span></a></h3><ul>
<li>事件是用户和浏览器之间的交互行为</li>
</ul><section><h4>鼠标事件<a href="#鼠标事件"><span>#</span></a></h4>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>onclick</td><td>当用户点击某个对象时调用的事件句柄。单击鼠标左键或者按下回车键时触发,意味着onclick事件处理程序既可以通过键盘也可以通过鼠标执行</td></tr><tr><td>oncontextmenu</td><td>在用户点击鼠标右键打开上下文菜单时触发。可以用做自定义菜单</td></tr><tr><td>ondblclick</td><td>当用户双击某个对象时调用的事件句柄。双击鼠标左键时触发</td></tr><tr><td>onmousedown</td><td>鼠标按钮被按下。</td></tr><tr><td>onmouseenter</td><td>当鼠标指针移动到元素上时触发。该事件不冒泡，即鼠标移到其后代元素上时不会触发</td></tr><tr><td>onmouseleave</td><td>当鼠标指针移出元素时触发。该事件不冒泡，即鼠标移出其后代元素时不会触发。</td></tr><tr><td>onmousemove</td><td>鼠标被移动。</td></tr><tr><td>onmouseover</td><td>鼠标移到某元素之上。鼠标移到其后代元素上时会触发,意味着支持冒泡</td></tr><tr><td>onmouseout</td><td>鼠标从某元素移开。支持冒泡</td></tr><tr><td>onmouseup</td><td>鼠标按键被松开</td></tr></tbody></table></section><section><h4>键盘事件<a href="#键盘事件"><span>#</span></a></h4>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>onkeydown</td><td>某个键盘按键被按下</td></tr><tr><td>onkeypress</td><td>某个键盘按键被按下并松开</td></tr><tr><td>onkeyup</td><td>某个键盘按键被松开</td></tr></tbody></table></section><section><h4>框架/对象事件<a href="#框架对象事件"><span>#</span></a></h4>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>onabort</td><td>图像的加载被中断。( <code>&lt;object&gt;)</code></td></tr><tr><td>onbeforeunload</td><td>该事件在即将离开页面（刷新或关闭）时触发 onerror 在加载文档或图像时发生错误。 ( , 和 )</td></tr><tr><td>onhashchange</td><td>该事件在当前 URL 的锚部分发生修改时触发</td></tr><tr><td>onload</td><td>一张页面或一幅图像完成加载</td></tr><tr><td>onpageshow</td><td>该事件在用户访问页面时触发</td></tr><tr><td>onpagehide</td><td>该事件在用户离开当前网页跳转到另外一个页面时触发</td></tr><tr><td>onresize</td><td>窗口或框架被重新调整大小。(由window调用)</td></tr><tr><td>onscroll</td><td>当文档被滚动时发生的事件</td></tr><tr><td>onunload</td><td>用户退出页面。 (<code>&lt;body&gt; </code>和 <code>&lt;frameset&gt;)</code></td></tr></tbody></table></section><section><h4>表单事件<a href="#表单事件"><span>#</span></a></h4>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>onblur</td><td>元素失去焦点时触发</td></tr><tr><td>onchange</td><td>该事件在表单元素的内容改变时触发( <code>&lt;input&gt;</code>, <code>&lt;keygen&gt;</code>, <code>&lt;select&gt;</code>, 和 <code>&lt;textarea&gt;</code>)</td></tr><tr><td>onfocus</td><td>元素获取焦点时触发</td></tr><tr><td>onfocusin</td><td>元素即将获取焦点时触发</td></tr><tr><td>onfocusout</td><td>元素即将失去焦点时触发</td></tr><tr><td>oninput</td><td>元素获取用户输入时触发</td></tr><tr><td>onreset</td><td>表单重置时触发</td></tr><tr><td>onsearch</td><td>用户向搜索域输入文本时触发 ( <code>&lt;input="search"&gt;</code>)</td></tr><tr><td>onselect</td><td>用户选取文本时触发 ( <code>&lt;input&gt;</code>和<code>&lt;textarea&gt;)</code></td></tr><tr><td>onsubmit</td><td>表单提交时触发</td></tr></tbody></table></section><section><h4>剪贴板事件<a href="#剪贴板事件"><span>#</span></a></h4>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>oncopy</td><td>该事件在用户拷贝元素内容时触发</td></tr><tr><td>oncut</td><td>该事件在用户剪切元素内容时触发</td></tr><tr><td>onpaste</td><td>该事件在用户粘贴元素内容时触发</td></tr></tbody></table></section><section><h4>打印事件<a href="#打印事件"><span>#</span></a></h4>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td>onafterprint</td><td>该事件在页面已经开始打印，或者打印窗口已经关闭时触发</td></tr><tr><td>onbeforeprint</td><td>该事件在页面即将开始打印时触发</td></tr></tbody></table></section><section><h4>拖动事件<a href="#拖动事件"><span>#</span></a></h4>

<table><thead><tr><th>事件</th><th>描述</th></tr></thead><tbody><tr><td>ondrag</td><td>该事件在元素正在拖动时触发</td></tr><tr><td>ondragend</td><td>该事件在用户完成元素的拖动时触发</td></tr><tr><td>ondragenter</td><td>该事件在拖动的元素进入放置目标时触发</td></tr><tr><td>ondragleave</td><td>该事件在拖动元素离开放置目标时触发</td></tr><tr><td>ondragover</td><td>该事件在拖动元素在放置目标上时触发</td></tr><tr><td>ondragstart</td><td>该事件在用户开始拖动元素时触发</td></tr><tr><td>ondrop</td><td>该事件在拖动元素放置在目标区域时触发</td></tr></tbody></table></section><section><h4>多媒体事件<a href="#多媒体事件"><span>#</span></a></h4>

<table><thead><tr><th>事件</th><th>描述</th></tr></thead><tbody><tr><td>onabort</td><td>事件在视频/音频（audio/video）终止加载时触发</td></tr><tr><td>oncanplay</td><td>事件在用户可以开始播放视频/音频（audio/video）时触发</td></tr><tr><td>oncanplaythrough</td><td>事件在视频/音频（audio/video）可以正常播放且无需停顿和缓冲时触发</td></tr><tr><td>ondurationchange</td><td>事件在视频/音频（audio/video）的时长发生变化时触发</td></tr><tr><td>onemptied</td><td>当期播放列表为空时触发</td></tr><tr><td>onended</td><td>事件在视频/音频（audio/video）播放结束时触发</td></tr><tr><td>onerror</td><td>事件在视频/音频（audio/video）数据加载期间发生错误时触发</td></tr><tr><td>onloadeddata</td><td>事件在浏览器加载视频/音频（audio/video）当前帧时触发触发</td></tr><tr><td>onloadedmetadata</td><td>事件在指定视频/音频（audio/video）的元数据加载后触发</td></tr><tr><td>onloadstart</td><td>事件在浏览器开始寻找指定视频/音频（audio/video）触发</td></tr><tr><td>onpause</td><td>事件在视频/音频（audio/video）暂停时触发</td></tr><tr><td>onplay</td><td>事件在视频/音频（audio/video）开始播放时触发</td></tr><tr><td>onplaying</td><td>事件在视频/音频（audio/video）暂停或者在缓冲后准备重新开始播放时触发</td></tr><tr><td>onprogress</td><td>事件在浏览器下载指定的视频/音频（audio/video）时触发</td></tr><tr><td>onratechange</td><td>事件在视频/音频（audio/video）的播放速度发送改变时触发</td></tr><tr><td>onseeked</td><td>事件在用户重新定位视频/音频（audio/video）的播放位置后触发</td></tr><tr><td>onseeking</td><td>事件在用户开始重新定位视频/音频（audio/video）时触发</td></tr><tr><td>onstalled</td><td>事件在浏览器获取媒体数据，但媒体数据不可用时触发</td></tr><tr><td>onsuspend</td><td>事件在浏览器读取媒体数据中止时触发</td></tr><tr><td>ontimeupdate</td><td>事件在当前的播放位置发送改变时触发</td></tr><tr><td>onvolumechange</td><td>事件在音量发生改变时触发</td></tr><tr><td>onwaiting</td><td>事件在视频由于要播放下一帧而需要缓冲时触发</td></tr></tbody></table></section><section><h4>动画事件<a href="#动画事件"><span>#</span></a></h4>

<table><thead><tr><th>事件</th><th>描述</th></tr></thead><tbody><tr><td>animationend</td><td>该事件在 CSS 动画结束播放时触发</td></tr><tr><td>animationiteration</td><td>该事件在 CSS 动画重复播放时触发</td></tr><tr><td>animationstart</td><td>该事件在 CSS 动画开始播放时触发</td></tr></tbody></table></section><section><h4>过渡事件<a href="#过渡事件"><span>#</span></a></h4>

<table><thead><tr><th>事件</th><th>描述</th></tr></thead><tbody><tr><td>transitionend</td><td>该事件在 CSS 完成过渡后触发</td></tr></tbody></table></section><section><h4>其他事件<a href="#其他事件"><span>#</span></a></h4>

<table><thead><tr><th>事件</th><th>描述</th></tr></thead><tbody><tr><td>onmessage</td><td>该事件通过或者从对象(WebSocket, Web Worker, Event Source 或者子 frame 或父窗口)接收到消息时触发</td></tr><tr><td>onmousewheel</td><td>已废弃。 使用 onwheel事件替代</td></tr><tr><td>ononline</td><td>该事件在浏览器开始在线工作时触发</td></tr><tr><td>onoffline</td><td>该事件在浏览器开始离线工作时触发</td></tr><tr><td>onpopstate</td><td>该事件在窗口的浏览历史（history 对象）发生改变时触发</td></tr><tr><td>onshow</td><td>该事件当 <code>&lt;menu&gt;</code> 元素在上下文菜单显示时触发 onstorage 该事件在 Web Storage(HTML 5 Web 存储)更新时触发</td></tr><tr><td>ontoggle</td><td>该事件在用户打开或关闭 <code>&lt;details&gt;</code>元素时触发</td></tr><tr><td>onwheel</td><td>该事件在鼠标滚轮在元素上下滚动时触发</td></tr><tr><td>DOMMouseScroll</td><td>火狐(firefox)支持的绑定鼠标滚轮的事件，并且该事件必须要通过addEventListener()函数来绑定事件</td></tr><tr><td>DOMContentLoaded</td><td>监听文档DOM元素的内容是否加载完成，比onload事件要监听的东西更少，DOM元素加载完毕后就会执行该事件,通过addEventListener()函数来绑定事件</td></tr></tbody></table><hr /></section></section><section><h3>DOM查询<a href="#dom查询"><span>#</span></a></h3><section><h4>获取DOM节点<a href="#获取dom节点"><span>#</span></a></h4><ul>
<li><code>getElementById()</code> 通过ID获取元素节点，返回一个普通对象
<ul>
<li><strong>注意:</strong> ID其实可以不用获取而直接使用,因为ID是具有唯一性的,但是不推荐不获取就直接使用ID，因为以后会很难区分这个变量是哪里来的</li>
</ul>
</li>
<li><code>getElementsByTagName()</code>  可以根据标签名来获取一组元素节点对象，这个方法会给我们返回一个类数组对象，所有查询到的元素都会封装到对象中，即使查询到的元素只有一个，也会封装到数组中返回</li>
<li><code>getElementsByClassName()</code> 方法通过类名获取元素节点(IE8及以下版本不支持),返回一个类数组对象</li>
<li><code>getElementsByName()</code> 方法通过name属性获取元素节点，这个方法主要是获取表单项，返回一个类数组对象</li>
<li><code>quertSelector()</code> 方法需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象，语法和CSS语法一样，可以多个选择器一起使用，该方法在IE8也可用
<ul>
<li>**注意:**使用该方法总会返回唯一的一个元素，如果满足条件的元素有多个，也只会返回第一个找到的</li>
</ul>
</li>
<li><code>quertSelectorAll()</code> 方法用法同quertSelector()方法相同
<ul>
<li>**注意:**使用该方法就返回一个类数组对象，类数组对象里包含的所有符号要求的元素对象,即使符合条件的元素只有一个，也会返回类数组</li>
</ul>
</li>
</ul></section><section><h4>获取属性节点<a href="#获取属性节点"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 读取元素的属性节点,使用 元素.属性名 或 元素[属性名]</span></div></div><div><div><div>2</div></div><div><span>- 修改元素的属性节点,使用 元素.属性名=新值 或 元素[属性名]=新值</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>- **注意:** 读取元素的class属性时必须用className来代替class关键字</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>获取文本节点<a href="#获取文本节点"><span>#</span></a></h4><ul>
<li><code>innerHTML</code> 属性可以获取双标签元素内部的html代码，包括子标签,这个属性对于单标签元素(如表单标签)没有意义，返回一个字符串</li>
<li><code>InnerText</code> 属性可以获取双标签元素内部的文本内容，它和InnerHTML属性类似，但会自动将HTML标签去除，返回一个字符串</li>
<li><code>value</code>属性可以获取单标签元素(如表单标签)内部的内容，同时要向单标签元素写入内容也必须使用value属性，返回一个字符串</li>
<li><code>nodeValue</code> 属性通过获取标签内的文本节点的内容来获取元素内部的文本内容(因为文本节点实际是一个标签的子节点，所以需要先找到文本节点，返回一个字符串</li>
</ul></section><section><h4>获取元素子节点<a href="#获取元素子节点"><span>#</span></a></h4><ul>
<li><code>childNodes</code>属性获取元素子节点，该属性会获取包括文本节点的所有子节点，返回一个类数组对象</li>
<li><code>children</code> 属性可以获取当前元素的所有子元素，推荐用这个属性，返回一个类数组对象，而且是动态获取</li>
<li><code>firstChild</code> 属性表示当前节点的第一个子节点，也包括空白文本，返回一个普通对象</li>
<li><code>firstElementChild</code> 属性获取第一个子元素，返回一个普通对象，但是不兼容IE8，不推荐使用</li>
<li><code>getElementsByTagName()</code> 方法获取当前节点下一组元素节点，返回一个类数组对象</li>
<li>注：
<ul>
<li>参数值 ” * ” 返回元素的所有子元素</li>
<li>document对象调用该方法，相当于获取整个文档中的元素节点</li>
</ul>
</li>
</ul></section><section><h4>获取父和兄弟节点<a href="#获取父和兄弟节点"><span>#</span></a></h4><ul>
<li><code>parentElement</code> 属性获取当前节点的父元素(只在IE中可用)</li>
<li><code>parentNode</code> 属性获取当前节点的父节点,这是W3C标准的,推荐使用</li>
<li><code>offsetParent</code>属性获取到离当前元素最近的开启了定位(除去默认的static)的祖先元素，如果所以的祖先元素都没有开启定位，则会返回body</li>
<li><code>previousSibling</code> 属性获取当前节点的前一个兄弟节点
<ul>
<li>**注意:**可能获取空白文本，如果两个元素中间有空白就会获取空白文本节点</li>
</ul>
</li>
<li><code>previosElementSibling</code> 属性获取前一个元素，IE8不支持</li>
<li><code>nextSibiling</code> 属性表示当前节点的后一个兄弟节点</li>
<li><code>nextElementSibling</code> 获取后一个元素，IE8不支持</li>
</ul></section><section><h4>获取元素节点的内容<a href="#获取元素节点的内容"><span>#</span></a></h4><ul>
<li><code>innerHTML</code>和<code>innerText</code> 属性
<ul>
<li>这两个属性并没有在DOM标准定义，但是大部分浏览器都支持这两个属性</li>
<li>两个属性作用类似，都可以获取到标签内部的内容，
<ul>
<li>不同是innerHTML会获取到html标签，而innerText会自动去除标签</li>
</ul>
</li>
<li>如果使用这两个属性来设置标签内部的内容时，没有任何区别的</li>
</ul>
</li>
<li><code>nodeValue</code> 属性设置或返回指定节点的节点值</li>
<li><strong>注</strong>：如果您希望返回元素的文本，请记住文本始终位于文本节点中，并且您必须返回文本节点的值
element.childNodes[0].nodeValue</li>
</ul></section><section><h4>document对象的其他的属性和方法<a href="#document对象的其他的属性和方法"><span>#</span></a></h4><ul>
<li><code>document.all</code> 获取页面中的所有元素，相当于document.getElementsByTagName(”*”);</li>
<li><code>document.documentElement</code> 获取页面中html根元素</li>
<li><code>document.body</code> 获取页面中的body元素</li>
</ul></section></section><section><h3>DOM修改<a href="#dom修改"><span>#</span></a></h3><section><h4>创建元素节点<a href="#创建元素节点"><span>#</span></a></h4><ul>
<li>
<p><code>document.createElement()</code> 方法可以根据标签名创建一个元素节点对象</p>
<ul>
<li>它需要一个标签名作为参数，将会根据该标签名创建元素节点对象，并将创建好的对象作为返回值返回
var div=document.createElement(“div”);</li>
</ul>
</li>
<li>
<p><code>document.createTextNode()</code> 可以根据文本内容创建一个文本节点对象</p>
<ul>
<li>需要一个文本内容作为参数，将会根据该内容创建文本节点，并将新节点返回
var divText = doucment.createTextNote(“adc”);</li>
</ul>
</li>
</ul></section><section><h4>添加元素节点<a href="#添加元素节点"><span>#</span></a></h4><ul>
<li><code>父节点.appendChild()</code> 向父节点中添加指定的子节点
<ul>
<li>要添加子节点之前必须要先有这个子节点，没有就要先创建，并且新加的子节点会自动添加到所有子节点的最后面</li>
</ul>
</li>
<li><code>父节点.insertBefore()</code> 将一个新的节点插入到旧节点的前边
<ul>
<li>该方法由父节点调用,传入两个参数(新节点和旧节点),第一个参数必填，第二个参数可选
父节点.insertBefore(新节点, 旧节点);</li>
<li>要添加子节点之前必须要先有这个子节点，没有就要先创建，并且如果没有传入第二个参数会自动添加到所有子节点的最后面</li>
<li>如果子节点不是新创建而是从原有父级节点上调用的，那么在用insertBefore()方法时会先将原有父级节点上的该子节点删除</li>
</ul>
</li>
</ul></section><section><h4>替换元素节点<a href="#替换元素节点"><span>#</span></a></h4><ul>
<li><code>父节点.replaceChild()</code> 使用一个新的节点去替换旧节点
<ul>
<li>要替换子节点之前必须要先有这个新的子节点，没有就要先创建
父节点.replaceChild(新节点,旧节点)</li>
</ul>
</li>
</ul></section><section><h4>删除元素节点<a href="#删除元素节点"><span>#</span></a></h4><ul>
<li><code>父节点.removeChild()</code>
<ul>
<li>删除指定的子节点</li>
<li>推荐方式：<code>子节点.parentNode.removeChild(子节点)</code></li>
</ul>
</li>
</ul></section></section><section><h3>元素样式<a href="#元素样式"><span>#</span></a></h3><section><h4>修改元素样式<a href="#修改元素样式"><span>#</span></a></h4><ul>
<li>通过<code>style</code>属性修改元素样式
<ul>
<li>
<p>语法：元素.style.样式名 = 样式值
<strong>注：</strong></p>
</li>
<li>
<p>通过style属性设置的样式都是<strong>内联样式</strong>，而内联样式有较高的优先级，所以通过JS修改的样式往往会立即显示</p>
</li>
<li>
<p>但是如果在样式中写了!important，则此时样式会有最高的优先级，即使通过JS也不能覆盖该样式，此时将会导致JS修改样式失效</p>
</li>
<li>
<p>如果CSS的样式名中含有 - ，这种名称在JS中是不合法的比如background-color，需要将这种样式名修改为驼峰命名法，去掉 - ，然后将 - 后的字母大写，如background-color写作backgroundColor，border-top-width写作borderTopWidth</p>
</li>
<li>
<p>通过JS的style属性设置和读取的都是内联样式，无法读取CSS样式表中的样式(外部样式和嵌套样式)</p>
</li>
</ul>
</li>
</ul></section><section><h4>读取元素样式<a href="#读取元素样式"><span>#</span></a></h4><ul>
<li>
<p><code>元素.currentStyle.样式名</code></p>
<ul>
<li>
<p>它可以用来读取当前元素正在显示的样式，如果当前元素没有设置该样式，则获取它的默认值，如：width返回auto</p>
</li>
<li>
<p>注：currentStyle<strong>只有IE浏览器支持，其他的浏览器都不支持</strong>
#box1{
width: 100px;
height: 100px;
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var box1 = document.getElementById("box1"); //获取box1元素</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>alert(box1.currentStyle.width);        //100px</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>alert(box1.currentStyle.backgroundColor);  //transparent</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
<li>
<p><code>getComputedStyle()</code></p>
<ul>
<li>
<p>获取元素当前的样式，这个方法是window的方法，可以直接使用，但是该方法<strong>不支持IE8及以下的浏览器</strong></p>
</li>
<li>
<p>需要两个参数</p>
<ul>
<li>第一个：要获取样式的元素</li>
<li>第二个：可以传递一个伪元素，一般都传null</li>
</ul>
</li>
<li>
<p>该方法会返回一个对象，对象中封装了当前元素对应的样式，可以通过<strong>对象.样式名</strong>来读取样式，如果获取的样式没有设置，则会获取到真实的值，而不是默认值，比如：没有设置width，它不会获取到auto，而是一个长度
#box1 {
width: 100px;
height: 200px;
background-color: #bfa;
}</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var box1 = document.getElementById("box1");</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>var objBox1 = getComputedStyle(box1, null);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>alert(objBox1.width);  //100px</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
<li>
<p><strong>注意:</strong> 通过currentStyle属性和getComputedStyle()方法读取到的样式都是只读的，不能修改，如果要修改必须通过style属性</p>
</li>
<li>
<p><strong>二者的兼容写法</strong>
function getStyle(obj , name){
/*
参数 要获取样式的元素
name 要获取的样式名
*/
if(window.getComputedStyle){ //这里的getComputedStyle函数实际上是一个对象，如果有则返回true，
而这里使用window对象调用，是在全局作用域中寻找是否含有这个对象没有则返回false（相当于ie没有该对象），
而如果不通过window对象调用的话，在局部作用域中调用会找不到这个对象，返回一个undefined
//正常浏览器的方式，具有getComputedStyle()方法
return getComputedStyle(obj , null)[name];
}
else{
//IE8的方式，没有getComputedStyle()方法
return obj.currentStyle[name];
}
//当然if的对象也可以反过来，但是如果判断obj.currentStyle有问题在于IE8以上的IE浏览器两种方法都有，
这样它就会优先使用第一个方法，但是我们推荐优先使用getComputedStyle()</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>//简洁写法，三目运算符</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>//return window.getComputedStyle?getComputedStyle(obj , null)[name]:obj.currentStyle[name];</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h4>获取特殊样式<a href="#获取特殊样式"><span>#</span></a></h4><ul>
<li>
<p><strong>注: 以下获取的样式都是只读的,不能够修改</strong></p>
</li>
<li>
<p><code>clientWidth</code> 与 <code>clientHeight</code></p>
<ul>
<li>这两个属性可以获取元素的可见宽度和高度</li>
<li>这些属性都是不带px的，返回都是一个number类型，可以直接进行计算</li>
<li>会获取元素宽度和高度，包括内容区和内边距</li>
</ul>
</li>
<li>
<p><code>offsetWidth</code> 与 <code>offsetHeight</code></p>
<ul>
<li>获取元素的整个宽度和高度(内容区，内边距和边框)，返回值也是number类型</li>
</ul>
</li>
<li>
<p><code>offsetParent</code></p>
<ul>
<li>可以用来获取当前元素的定位父元素</li>
<li>会获取到离当前元素最近的开启了定位的祖先元素，如果所有的祖先元素都没有开启定位，则返回body</li>
</ul>
</li>
<li>
<p><code>offsetLeft</code> 与 <code>offsetTop</code></p>
<ul>
<li>获取当前元素相对于其定位父元素的水平偏移量和垂直偏移量</li>
<li>**注意：**偏移量的原点是父元素的左上角(left top)，和背景图片的原点相同</li>
</ul>
</li>
<li>
<p><code>window.innerWidth</code> 与 <code>window.innerHeight</code></p>
<ul>
<li>可以获取window窗口的内部宽高</li>
<li>**注意：**不包括页面的导航栏以及页面滚动条和控制台</li>
</ul>
</li>
<li>
<p><code>scrollWidth</code> 与 <code>scrollHeight</code></p>
<ul>
<li>可以获取元素整个滚动区域的宽度和高度，如果没有隐藏的部分则等于clientWidth和clientHeight</li>
</ul>
</li>
<li>
<p><code>scrollLeft</code> 与 <code>scrollTop</code></p>
<ul>
<li>可以获取水平和垂直滚动条滚动的距离
当满足 scrollHeight - scrollTop == clientHeight，说明垂直滚动条滚动到底了（实际高度 - 滚动高度 == 可视高度）
当满足 scrollWidth - scrollLeft == clientWidth，说明水平滚动条滚动到底</li>
</ul>
</li>
<li>
<p>实例

Document

</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>                  </span></span><span>input[0].onclick = function () {  //勾选checkbox时</span></div></div><div><div><div>2</div></div><div><span><span>                      </span></span><span>if (input[0].checked == true) {</span></div></div><div><div><div>3</div></div><div><span><span>                          </span></span><span>input[1].disabled = false;</span></div></div><div><div><div>4</div></div><div><span><span>                      </span></span><span>}</span></div></div><div><div><div>5</div></div><div><span><span>                      </span></span><span>else{</span></div></div><div><div><div>6</div></div><div><span><span>                          </span></span><span>input[1].disabled = true;</span></div></div><div><div><div>7</div></div><div><span><span>                      </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>                  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>              </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;/script&gt;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>&lt;style&gt;</span></div></div><div><div><div>15</div></div><div><span><span>          </span></span><span>#info{</span></div></div><div><div><div>16</div></div><div><span><span>              </span></span><span>width: 200px;</span></div></div><div><div><div>17</div></div><div><span><span>              </span></span><span>height: 300px;</span></div></div><div><div><div>18</div></div><div><span><span>              </span></span><span>background-color: #bbffaa;</span></div></div><div><div><div>19</div></div><div><span><span>              </span></span><span>overflow: auto;</span></div></div><div><div><div>20</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>&lt;/style&gt;</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>&lt;/head&gt;</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>&lt;body&gt;</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>&lt;div id="box"&gt;</span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>&lt;p id="info"&gt;亲爱的用户，请仔细阅读以下协议，如果你不仔细阅读你就别注册</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>&lt;/p&gt;</span></div></div><div><div><div>27</div></div><div><span><span>              </span></span><span>&lt;input type="checkbox" name="c" id="check" disabled="disabled"&gt;我已阅读</span></div></div><div><div><div>28</div></div><div><span><span>              </span></span><span>&lt;input type="button" value="注册" disabled="disabled" &gt;</span></div></div><div><div><div>29</div></div><div><span><span>      </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>&lt;/body&gt;</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>&lt;/html&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/zi-fu-chuan-fang-fa/</id>
      <title type="text">字符串方法</title>
      <published>2021-04-28T00:25:31.000Z</published>
      <updated>2021-04-28T00:25:31.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/zi-fu-chuan-fang-fa/"/>
      <summary type="text">在底层字符串是以字符数组的形式保存的 var str = &quot;abc&quot;;// [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;] length属性 可以用来获取字符串的长度 console.log(str.length); //输出字符串str的长度</summary>
      <content type="html"><![CDATA[<section><h2>字符串的相关方法<a href="#字符串的相关方法"><span>#</span></a></h2><ul>
<li>在底层字符串是以字符数组的形式保存的
var str = “abc”;// [“a”,“b”,“c”]</li>
<li>length属性
<ul>
<li>可以用来获取字符串的长度
console.log(str.length); //输出字符串str的长度</li>
</ul>
</li>
</ul><section><h3>charAt()<a href="#charat"><span>#</span></a></h3><ul>
<li>
<p>可以返回字符串中指定位置的字符</p>
</li>
<li>
<p>根据索引获取指定的字符
var str = “aBcd”;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log(str.charAt(2)); //c</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(str[2]); //c</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>charCodeAt()<a href="#charcodeat"><span>#</span></a></h3><ul>
<li>获取指定位置字符的字符编码（Unicode编码）
var str = “aBcd”;
console.log(str.charCodeAt(1)); // 66</li>
</ul></section><section><h3>String.formCharCode()<a href="#stringformcharcode"><span>#</span></a></h3><ul>
<li>可以根据字符编码去获取字符
var str = “aBcd”;
console.log(String.fromCharCode(66)); // B</li>
</ul></section><section><h3>concat()<a href="#concat"><span>#</span></a></h3><ul>
<li>
<p>可以用来连接两个或多个字符串（不会对改变原字符）</p>
</li>
<li>
<p>作用和 + 一样
var str = “aBcd”;
var str2 = “EfG”;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log(str.concat(str2, )); // aBcdEfG</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(str); // aBcd</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>console.log(str.concat(str2, "123")); // aBcdEfG123</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>indexof()<a href="#indexof"><span>#</span></a></h3><ul>
<li>该方法可以检索一个字符串中是否含有指定内容
<ul>
<li>如果字符串中含有该内容，则会返回其第一次出现的索引,如果没有找到指定的内容，则返回-1</li>
<li>可以指定一个第二个参数，指定开始查找的位置
var str = “Abc abcd”;
console.log(str.indexOf(“b”));	// 1
console.log(str.indexOf(“b”, 2)); // 5 从第二个位置开始查找</li>
</ul>
</li>
</ul></section><section><h3>lastIndexOf()<a href="#lastindexof"><span>#</span></a></h3><ul>
<li>该方法的用法和indexOf()一样，不同的是indexOf是从前往后找，而lastIndexOf是从后往前找
<ul>
<li>可以指定一个第二个参数，指定开始查找的位置
var str = “Abc abcd”;
console.log(str.lastIndexOf(“b”)); // 5
console.log(str.lastIndexOf(“b”, 4)); // 1 从第二个位置开始从后往前查找</li>
</ul>
</li>
</ul></section><section><h3>slice()<a href="#slice"><span>#</span></a></h3><ul>
<li>可以从字符串中截取指定的内容</li>
<li>不会影响原字符串，而是将截取到内容返回</li>
<li>参数：
第一个，开始位置的索引（包括开始位置）
第二个，结束位置的索引（不包括结束位置）
<ul>
<li>
<p>如果省略第二个参数，则会截取到后边所有的</p>
</li>
<li>
<p>也可以传递一个负数作为参数，负数的话将会从后边计算
str = “abcdefghijk”;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log(str.slice(1,4)); //bcd</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(str.slice(1,-1)); //bcdefghij</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul></section><section><h3>substring()<a href="#substring"><span>#</span></a></h3><ul>
<li>可以用来截取一个字符串，可以slice()类似</li>
<li>参数：
<ul>
<li>第一个：开始截取位置的索引（包括开始位置）</li>
<li>第二个：结束位置的索引（不包括结束位置）</li>
<li>不同的是这个方法不能接受负值作为参数，如果传递了一个负值，则默认使用0，而且他还自动调整参数的位置，如果第二个参数小于第一个，则自动交换
var str = “abcdefghijk”;
console.log(str.substring(0, 2)); // ab
console.log(str.substring(2, 0)); // ab</li>
</ul>
</li>
</ul></section><section><h3>substr()<a href="#substr"><span>#</span></a></h3><ul>
<li>用来截取字符串</li>
<li>参数：
<ul>
<li>第一个：截取开始位置的索引</li>
<li>第二个：截取的长度
var str = “abcdefghijk”;
console.log(str.substr(2, 3)); // cde</li>
</ul>
</li>
</ul></section><section><h3>split()<a href="#split"><span>#</span></a></h3><ul>
<li>可以将一个字符串拆分为一个数组</li>
<li>参数：
<ul>
<li>
<p>需要一个字符串作为参数，将会根据该字符串去拆分数组</p>
</li>
<li>
<p>如果传递一个空串作为参数，则会将每个字符都拆分为数组中的一个元素
var str = “abcbcdefgdhij”;
console.log(str.split(“d”));  // [ ‘abcbc’, ‘efg’, ‘hij’ ]</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var str2 = "abc";</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(str2.split("")); // [ 'a', 'b', 'c' ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul></section><section><h3>toUpperCase()<a href="#touppercase"><span>#</span></a></h3><ul>
<li>将一个字符串转换为大写并返回
var str = “Abc”;
console.log(str.toUpperCase()); // ABC</li>
</ul></section><section><h3>toLowerCase()<a href="#tolowercase"><span>#</span></a></h3><ul>
<li>将一个字符串转换为小写并返回
var str = “Abc”;
console.log(str.toLowerCase()); // abc</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/zheng-ze-biao-da-shi/</id>
      <title type="text">正则表达式</title>
      <published>2021-04-28T00:23:30.000Z</published>
      <updated>2021-04-28T00:23:30.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/zheng-ze-biao-da-shi/"/>
      <summary type="text">正则表达式用于定义一些字符串的规则，计算机可以根据正则表达式，来检查一个字符串是否符合规则，获取将字符串中符合规则的内容提取出来</summary>
      <content type="html"><![CDATA[<p>正则表达式用于定义一些字符串的规则，计算机可以根据正则表达式，来检查一个字符串是否符合规则，获取将字符串中符合规则的内容提取出来</p>
<section><h2>创建正则表达式的对象<a href="#创建正则表达式的对象"><span>#</span></a></h2><section><h3>构造函数方式构造<a href="#构造函数方式构造"><span>#</span></a></h3><ul>
<li>语法：
var 变量 = new RegExp(“正则表达式”,“匹配模式”);
var reg = new RegExp(“a”); //这个正则表达式可以来检查一个字符串中是否含有a
var reg = new RegExp(“a”, “i”); //不区分大小写</li>
<li>使用typeof检查正则对象，会返回object</li>
<li>在构造函数中可以传递一个匹配模式作为第二个参数，可以是
i 忽略大小写
g 全局匹配模式</li>
</ul></section><section><h3>使用字面量来创建<a href="#使用字面量来创建"><span>#</span></a></h3><ul>
<li>语法：
var 变量 = /正则表达式/匹配模式
var reg = /a/;  //等价于var reg = new RegExp(“a”);
var reg = /a/i; //等价于var reg = new RegExp(“a”, “i”);</li>
</ul></section></section>
<section><h2>正则表达式的语法<a href="#正则表达式的语法"><span>#</span></a></h2><section><h3>使用 | 或 [] 表示或者的意思<a href="#使用--或--表示或者的意思"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//下面三个表达式等价</span></div></div><div><div><div>2</div></div><div><span>var reg = /a|b|c/</span></div></div><div><div><div>3</div></div><div><span>var reg = /[abc]/</span></div></div><div><div><div>4</div></div><div><span>var reg = /[a-c]/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>[a-z] 任意小写字母</li>
<li>[A-Z] 任意大写字母</li>
<li>[A-z] 任意字母</li>
<li>[0-9] 任意数字</li>
<li>[^ ]  非</li>
</ul></section><section><h3>量词<a href="#量词"><span>#</span></a></h3><ul>
<li>
<p>通过量词可以设置一个内容出现的次数</p>
</li>
<li>
<p>量词只对它前边的一个内容起作用</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>{n}    正好出现n次</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>{m,n}  出现m-n次</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>{m,}  m次以上</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>\+    至少一个，相当于{1,}</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>\*    0个或多个，相当于{0,}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>?    0个或1个，相当于{0,1}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>检查一个字符串中开头或结尾<a href="#检查一个字符串中开头或结尾"><span>#</span></a></h3><ul>
<li>
<p>^ 表示开头</p>
</li>
<li>
<p>$ 表示结尾</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var reg = /^a/; //以a开始</span></div></div><div><div><div>2</div></div><div><span>var reg = /a$/; //以a结尾</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果在正则表达式中同时使用^ $则要求字符串必须完全符合正则表达式</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var reg = /^a&amp;/; //要求正则表达式必须为a</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>使用 \ 作为转义字符<a href="#使用--作为转义字符"><span>#</span></a></h3><ul>
<li><code>\\\.</code> 表示 <code>.</code></li>
<li><code>\\\\</code>  表示 <code>\\</code></li>
</ul></section><section><h3>元字符<a href="#元字符"><span>#</span></a></h3><ul>
<li>
<p><code>.</code>  单个字符，除了换行和行结束符</p>
</li>
<li>
<p><code>\w</code> 任意字母、数字、_  [A-z0-9_]</p>
</li>
<li>
<p><code>\W</code> 非字母、数字、_  [^A-z0-9_]</p>
</li>
<li>
<p><code>\d</code> 任意的数字 [0-9]</p>
</li>
<li>
<p><code>\D</code> 非数字 [^0-9]</p>
</li>
<li>
<p><code>\s</code> 空格</p>
</li>
<li>
<p><code>\S</code> 非空格</p>
</li>
<li>
<p><code>\b</code> 单词边界</p>
</li>
<li>
<p><code>\B</code> 非单词边界</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>电子邮件</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>hello  .nihao          @     abc  .com.cn</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>任意字母数字下划线    .任意字母数字下划线  @   任意字母数字     .任意字母（2-5位）   .任意字母（2-5位）</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>\w{3,}  (\.\w+)*  @  [A-z0-9]+  (\.[A-z]{2,5}){1,2}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>var emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>var email = "abc.hello@163.com";</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>console.log(emailReg.test(email)); //true</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section></section>
<section><h2>正则表达式的方法<a href="#正则表达式的方法"><span>#</span></a></h2><section><h3>test()<a href="#test"><span>#</span></a></h3><ul>
<li>
<p>使用这个方法可以用来检查一个字符串是否符合正则表达式的规则，如果符合则返回true，否则返回false
var str = “abc123”;
var reg = new RegExp(“aB”, “i”);</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>console.log(reg.test(str)); //true</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>split()<a href="#split"><span>#</span></a></h3><ul>
<li>可以将一个字符串拆分为一个数组</li>
<li>方法中可以传递一个正则表达式作为参数，这样方法将会根据正则表达式去拆分字符串</li>
<li>这个方法即使不指定全局匹配，也会全都拆分
//根据任意字母来将字符串拆分
var str = “1a2b3c4d5e6f7”;
console.log(str.split(/[A-z]/)); //[‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’]</li>
</ul></section><section><h3>search()<a href="#search"><span>#</span></a></h3><ul>
<li>可以搜索字符串中是否含有指定内容</li>
<li>如果搜索到指定内容，则会返回第一次出现的索引，如果没有搜索到返回-1</li>
<li>它可以接受一个正则表达式作为参数，然后会根据正则表达式去检索字符串</li>
<li>serach()只会查找第一个，即使设置全局匹配也没用
//搜索字符串中是否含有abc 或 aec 或 afc
str = “hello abc hello aec afc”;
console.log(str.search(/a[bef]c/)); //6</li>
</ul></section><section><h3>match()<a href="#match"><span>#</span></a></h3><ul>
<li>可以根据正则表达式，从一个字符串中将符合条件的内容提取出来</li>
<li>默认情况下我们的match只会找到第一个符合要求的内容，找到以后就停止检索
<ul>
<li>我们可以设置正则表达式为全局匹配模式，这样就会匹配到所有的内容</li>
<li>可以为一个正则表达式设置多个匹配模式，且顺序无所谓</li>
</ul>
</li>
<li>match()会将匹配到的内容封装到一个数组中返回，即使只查询到一个结果
var str = “1a2a3a4a5e6f7A8B9C”;
//提取字符串中所有的字母（不区分大小写）
console.log(str.match(/[a-z]/ig)); //返回值是数组</li>
</ul></section><section><h3>replace()<a href="#replace"><span>#</span></a></h3><ul>
<li>可以将字符串中指定内容替换为新的内容</li>
<li>参数：
<ul>
<li>第一个：被替换的内容，可以接受一个正则表达式作为参数</li>
<li>第二个：新的内容</li>
</ul>
</li>
<li>默认只会替换第一个，设置正则表达式为全局匹配模式，这样就会替换到所有符合正则表达式的内容</li>
<li>第二个参数设为空串，相当于删除匹配到的内容
var str = “1a2a3a4a5e6f7A8B9C”;
console.log(str.replace(/[a-z]/gi, ”@”)); // 1@2@3@4@5@6@7@8@9@
console.log(str.replace(/[a-z]/gi, ""));  // 123456789</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/shu-zu/</id>
      <title type="text">数组</title>
      <published>2021-04-28T00:21:47.000Z</published>
      <updated>2021-04-28T00:21:47.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/shu-zu/"/>
      <summary type="text">数组也是一个对象 普通对象是使用字符串作为属性名的，而数组时使用数字来作为索引操作元素 数组中的元素可以是任意的数据类型，可以是对象</summary>
      <content type="html"><![CDATA[<ul>
<li>数组也是一个对象</li>
<li>普通对象是使用字符串作为属性名的，而数组时使用数字来作为索引操作元素</li>
<li>数组中的元素可以是任意的数据类型，可以是对象</li>
</ul>
<section><h2>创建数组<a href="#创建数组"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 声明或创建一个不指定长度的数组</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>var arr = new Array();</span></div></div><div><div><div>3</div></div><div><span>- 声明或创建一个指定长度的数组</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>var arr = new Array(10); //创建一个length为10的数组，没有值</span></div></div><div><div><div>5</div></div><div><span>- 声明或创建一个带有默认值的数组</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>var arr = new Array(1, 2, 3); //length为3，值为[1, 2, 3]</span></div></div><div><div><div>7</div></div><div><span>- 使用字面量创建数组</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>var arr = [1, 2, 3, 4, 5] //length为5，值为[1, 2, 3, 4, 5]</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>//创建不定长度的数组</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>var arr2 = [];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>
<p><strong>读取数组中的元素</strong></p>
<ul>
<li>语法：
数组[索引]
arr[2]</li>
<li>如果读取不存在的索引，他不会报错而是返回undefined</li>
</ul>
</li>
<li>
<p><strong>length</strong></p>
<ul>
<li>
<p>length是数组的一个属性,代表数组的长度</p>
</li>
<li>
<p>获取数组的长度</p>
<ul>
<li>
<p>可以使用length属性来获取数组的长度(元素的个数)</p>
</li>
<li>
<p>语法：
数组.length</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var arr=[1,2,3]; //arr.length = 3;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log(arr.length);//输出数组长度</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>对于连续的数组，使用length可以获取到数组的长度（元素的个数）</p>
</li>
<li>
<p>对于非连续的数组，使用length会获取到数组的最大的索引+1</p>
</li>
<li>
<p>尽量不要创建非连续的数组</p>
</li>
</ul>
</li>
<li>
<p>修改length</p>
<ul>
<li>如果修改的length大于原长度，则多出部分会空出来</li>
<li>如果修改的length小于原长度，则多出的元素会被删除，所以可以修改length的长度为0来让数组为空</li>
</ul>
</li>
<li>
<p>向数组的最后一个位置添加元素</p>
<ul>
<li>
<p>语法：
数组[数组.length] = 值;</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>arr[arr.length] = 10;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul>
</li>
</ul></section>
<section><h2>数组的方法<a href="#数组的方法"><span>#</span></a></h2><section><h3>push()<a href="#push"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 该方法可以向数组的末尾添加一个或多个元素，并返回数组的新的长度</span></div></div><div><div><div>2</div></div><div><span>- 可以将要添加的元素作为方法的参数传递，这样这些元素将会自动添加到数组的末尾</span></div></div><div><div><div>3</div></div><div><span>- 该方法会将数组新的长度作为返回值返回</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>var arr = [1, 2, 3];</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>var result = arr.push(0);//arr = [1, 2, 3, 4], result = 4</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>pop()<a href="#pop"><span>#</span></a></h3><ul>
<li>该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
var arr = [1, 2, 3];
var result = arr.pop(); // result = 3, arr = [1, 2]</li>
</ul></section><section><h3>unshift()<a href="#unshift"><span>#</span></a></h3><ul>
<li>向数组开头添加一个或多个元素，并返回新的数组长度</li>
<li>向前边插入元素以后，其他的元素索引会依次调整
var arr = [1, 2, 3];
var result = arr.unshift(4, 5); // result = 5, arr = [ 4, 5, 1, 2, 3 ]</li>
</ul></section><section><h3>shift()<a href="#shift"><span>#</span></a></h3><ul>
<li>可以删除数组的第一个元素，并将被删除的元素作为返回值返回
var arr = [1, 2, 3];
var result = arr.shift(); // result = 1, arr = [2, 3]</li>
</ul></section><section><h3>slice()<a href="#slice"><span>#</span></a></h3><ul>
<li>
<p>可以用来从数组提取指定元素</p>
</li>
<li>
<p>该方法<strong>不会改变元素数组</strong>， 而是将截取到的元素封装到一个新数组中返回
参数：
第一个，截取开始的位置的索引, 包含开始索引
第二个，截取结束的位置的索引, 不包含结束索引</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>var arr = ["a", "b", "c", "d"];</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>var result = arr.slice(2, 3); // result = [ 'c' ], arr = [ 'a', 'b', 'c', 'd' ]</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>result = arr.slice(2); // result =  [ 'c', 'd' ], arr = [ 'a', 'b', 'c', 'd' ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>第二个参数可以省略不写, 此时会截取从开始索引往后的所有元素</p>
</li>
<li>
<p>索引可以传递一个负值， 如果传递一个负值， 则从后往前计算
- 1 倒数第一个
- 2 倒数第二个</p>
</li>
</ul></section><section><h3>splice()<a href="#splice"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- 可以用于删除数组中的指定元素，并向数组中添加新元素</span></div></div><div><div><div>2</div></div><div><span>- 使用splice()会**影响到原数组**，会将指定元素从原数组中删除，并将被删除的元素作为返回值返回</span></div></div><div><div><div>3</div></div><div><span>- 参数：</span></div></div><div><div><div>4</div></div><div><span>- 第一个，表示开始位置的索引</span></div></div><div><div><div>5</div></div><div><span>- 第二个，表示删除的数量</span></div></div><div><div><div>6</div></div><div><span>- 第三个及以后。。</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>可以传递一些新的元素，这些元素将会自动插入到开始位置索引前边</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>var arr = ["a", "b", "c", "d"];</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>var result  = arr.splice(1,2); // result = [ 'b', 'c' ], arr = [ 'a', 'd' ]</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>var result  = arr.splice(1, 2, "e", "f"); // result = [ 'b', 'c' ], arr = [ 'a', 'e', 'f', 'd' ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>concat()<a href="#concat"><span>#</span></a></h3><ul>
<li>
<p>concat()可以连接两个或多个数组，并将新的数组返回</p>
</li>
<li>
<p>该方法<strong>不会对原数组产生影响</strong>
var arr1 = [“a”, “b”, “c”];
var arr2 = [“1”, “2”, “3”];</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var result = arr1.concat(arr2); // result = [ 'a', 'b', 'c', '1', '2', '3' ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>join()<a href="#join"><span>#</span></a></h3><ul>
<li>
<p>该方法可以将数组转换为一个字符串</p>
</li>
<li>
<p>该方法<strong>不会对原数组产生影响</strong>，而是将转换后的字符串作为结果返回</p>
</li>
<li>
<p>在join()中可以指定一个字符串作为参数，这个字符串将会成为数组中元素的连接符，如果不指定连接符，则默认使用,作为连接符，如果指定为空，则会直接将字符连接
var arr1 = [“a”, “b”, “c”];</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var result = arr1.join();// result = a,b,c</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>var result = arr1.join("@_&amp;"); // result = a@_&amp;b@_&amp;c</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>var result = arr1.join(""); // result = abc</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>reverse()<a href="#reverse"><span>#</span></a></h3><ul>
<li>
<p>该方法用来反转数组（前边的去后边，后边的去前边）</p>
</li>
<li>
<p>该方法会<strong>直接修改原数组</strong>
var arr1 = [“a”, “b”, “c”];</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>var result = arr1.reverse();</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span> </span></span><span>console.log(result); //result = [ 'c', 'b', 'a' ]</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>console.log(arr1); // arr1 = [ 'c', 'b', 'a' ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>sort()<a href="#sort"><span>#</span></a></h3><ul>
<li>
<p>可以用来对数组中的元素进行排序</p>
</li>
<li>
<p>也会<strong>影响原数组</strong>，默认会按照Unicode编码进行排序
//字符型数组
var arr1 = [“b”, “d”, “a”, “c”];
var result = arr1.sort();
console.log(result); //[ ‘a’, ‘b’, ‘c’, ‘d’ ]
console.log(arr1); //[ ‘a’, ‘b’, ‘c’, ‘d’ ]</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>//number型数组，以Unicode编码进行排序</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>var arr2 = [2, 5, 7, 21, 13];</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>var result = arr2.sort();</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>console.log(result); //[ 13, 2, 21, 5, 7 ]</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>console.log(arr2); // [ 13, 2, 21, 5, 7 ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>对于number型数组，我们可以自己来指定排序的规则</p>
<ul>
<li>
<p>我们可以在sort()添加一个回调函数，来指定排序规则，回调函数中需要定义两个形参，浏览器将会分别使用数组中的元素作为实参去调用回调函数</p>
</li>
<li>
<p>浏览器会根据回调函数的返回值来决定元素的顺序:</p>
<ul>
<li>
<p>如果返回一个大于0的值，则元素会交换位置</p>
</li>
<li>
<p>如果返回一个小于0的值，则元素位置不变</p>
</li>
<li>
<p>如果返回一个0，则认为两个元素相等，也不交换位置</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var result = arr2.sort(function(a, b){</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>/*</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>if(a &gt; b){</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>return -1;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>else if(a &lt; b){</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>return 1;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>else{</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>return 0;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>/*</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>return a - b;//升序，return b - a 则降序</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>console.log(result); //[ 2, 5, 7, 13, 21 ]</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>console.log(arr2); //[ 2, 5, 7, 13, 21 ]</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul>
<p>如果需要升序排列，则返回 a - b
如果需要降序排列，则返回 b - a</p>
</li>
</ul>
</li>
</ul></section></section>
<section><h2>数组的遍历<a href="#数组的遍历"><span>#</span></a></h2><section><h3>forEach()<a href="#foreach"><span>#</span></a></h3><ul>
<li>
<p>forEach()方法需要一个函数作为参数</p>
</li>
<li>
<p>数组中有几个元素函数就会执行几次，每次执行时，浏览器会将遍历到的元素以实参的形式传递进来，我们可以来定义形参，来读取这些内容</p>
</li>
<li>
<p>浏览器会在回调函数中传递三个参数：</p>
<ul>
<li>第一个参数（value），就是当前正在遍历的元素</li>
<li>第二个参数（index），就是当前正在遍历的元素的索引</li>
<li>第三个参数（object），就是正在遍历的数组
arr.forEach(function(value , index , obj){
console.log(value);
});</li>
</ul>
</li>
<li>
<p>列如：
去除数组arr中重复的数字
var arr = [1,2,3,2,2,1,3,4,2,5];</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>//for循环方式遍历</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>for(var i = 0; i &lt; arr.length; i++){</span></div></div><div><div><div>3</div></div><div><span><span>      </span></span><span>for(var j = i + 1; j &lt; arr.length; j++){</span></div></div><div><div><div>4</div></div><div><span><span>          </span></span><span>if(arr[i] == arr[j]){</span></div></div><div><div><div>5</div></div><div><span><span>              </span></span><span>arr.splice(j,1);</span></div></div><div><div><div>6</div></div><div><span><span>              </span></span><span>j--;</span></div></div><div><div><div>7</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>//forEach方式遍历</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>arr.forEach(function(num, i, arr){  //num相当于arr[i]</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>for(var j = i + 1; j &lt; arr.length - 1; j++){</span></div></div><div><div><div>14</div></div><div><span><span>          </span></span><span>if(num == arr[j]){</span></div></div><div><div><div>15</div></div><div><span><span>              </span></span><span>arr.splice(j, 1);</span></div></div><div><div><div>16</div></div><div><span><span>              </span></span><span>j--;</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>console.log(arr);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/lei-xing-zhuan-huan/</id>
      <title type="text">类型转换</title>
      <published>2021-04-28T00:18:39.000Z</published>
      <updated>2021-04-28T00:18:39.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/lei-xing-zhuan-huan/"/>
      <summary type="text">类型转换就是指将其他的数据类型，转换为String Number 或 Boolean</summary>
      <content type="html"><![CDATA[<ul>
<li>类型转换就是指将其他的数据类型，转换为String Number 或 Boolean</li>
</ul>
<section><h2>转换为String<a href="#转换为string"><span>#</span></a></h2><ul>
<li>
<p>方式一（强制类型转换）：</p>
<ul>
<li>调用被转换数据的toString()方法</li>
<li>例子：
var a = 123;
a = a.toString();</li>
<li>注意：这个方法不适用于null和undefined
由于这两个类型的数据中没有方法，所以调用toString()时会报错</li>
</ul>
</li>
<li>
<p>方式二（强制类型转换）：</p>
<ul>
<li>调用String()函数</li>
<li>例子：
var a = 123;
a = String(a);</li>
<li>原理：对于Number Boolean String都会调用他们的toString()方法来将其转换为字符串，
<ul>
<li>对于null值，直接转换为字符串”null”。</li>
<li>对于undefined，直接转换为字符串”undefined”</li>
</ul>
</li>
</ul>
</li>
<li>
<p>方式三（隐式的类型转换）:</p>
<ul>
<li>为任意的数据类型 +""</li>
<li>例子：
var a = true;
a = a + "";</li>
<li>原理：和String()函数一样</li>
</ul>
</li>
</ul></section>
<section><h2>转换为Number<a href="#转换为number"><span>#</span></a></h2><ul>
<li>
<p>方式一（强制类型转换）：</p>
<ul>
<li>调用Number()函数</li>
<li>例子：
var s = “123”;
s = Number(s);</li>
<li>转换的情况：
1.字符串 —&gt; 数字
如果字符串是一个合法的数字，则直接转换为对应的数字
如果字符串是一个非法的数字，则转换为NaN
如果是一个空串或纯空格的字符串，则转换为0
2.布尔值 —&gt; 数字
true 转换为 1
false 转换为 0
3.空值 null —&gt; 数字
null 转换为 0
4.未定义 undefind—&gt; 数字
undefined 转换为 NaN</li>
</ul>
</li>
<li>
<p>方式二（强制类型转换）：</p>
<ul>
<li>
<p>调用parseInt()或parseFloat()，这两个函数专门用来将一个字符串转换为数字</p>
</li>
<li>
<p>parseInt()</p>
<ul>
<li>可以将一个字符串中的有效的整数位提取出来，并转换为Number</li>
<li>例子：
var a = “123.456px”;
a = parseInt(a); //123</li>
<li>如果需要可以在parseInt()中指定一个第二个参数，来指定进制
var a = 0xff;
a = parseInt(a, 10);//255 将16进制a转换为10进制</li>
</ul>
</li>
<li>
<p>parseFloat()</p>
<ul>
<li>
<p>可以将一个字符串中的有效的小数位提取出来，并转换为Number</p>
</li>
<li>
<p>例子：
var a = “123.456px”;
a = parseFloat(a); //123.456
注：对非String类型的变量使用parseInt()或parseFloat()方法，会先将变量转换为String类型，然后才转换为number类型</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var a = true;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a = parseInt(a); //NaN</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>方式三（隐式的类型转换）：</p>
<ul>
<li>
<p>使用一元的+来进行隐式的类型转换</p>
</li>
<li>
<p>例子：
var a = “123”;
a = +a;</p>
</li>
<li>
<p>原理：和Number()函数一样</p>
</li>
</ul>
</li>
</ul></section>
<section><h2>转换为布尔值<a href="#转换为布尔值"><span>#</span></a></h2><ul>
<li>
<p>方式一（强制类型转换）：</p>
<ul>
<li>使用Boolean()函数</li>
<li>例子：
var s = “false”;
s = Boolean(s); //true</li>
<li>转换的情况
<ul>
<li>
<p>字符串 —&gt; 布尔
除了空串其余全是true</p>
</li>
<li>
<p>数值 —&gt; 布尔
除了0和NaN其余的全是true</p>
</li>
<li>
<p>对象 ---&gt; 布尔
都是true</p>
</li>
<li>
<p>null、undefined ---&gt; 布尔
都是false</p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>方式二（隐式类型转换）：</p>
<ul>
<li>为任意的数据类型做两次非运算，即可将其转换为布尔值</li>
<li>例子：
var a = “hello”;
a = !!a; //true</li>
</ul>
</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://sukksa.github.io/posts/ji-ben-yu-fa/</id>
      <title type="text">基本语法</title>
      <published>2021-04-28T00:15:36.000Z</published>
      <updated>2021-04-28T00:15:36.000Z</updated>
      <author><name>Sukksa</name></author>
      <link rel="alternate" href="https://sukksa.github.io/posts/ji-ben-yu-fa/"/>
      <summary type="text">可以将js代码编写到标签的onclick属性或超链接的href属性中（当我们点击按钮时，js代码才会执行 可以将js代码编写到script标签 + 可以将js代码编写到外部js文件中，然后通过script标签引入 注：script标签一旦用于引入外部文件了，就不能在编写代码了，即使编写了浏览器也会忽…</summary>
      <content type="html"><![CDATA[<section><h1>JavaScript的基本语法<a href="#javascript的基本语法"><span>#</span></a></h1><section><h2>JavaScript编写位置<a href="#javascript编写位置"><span>#</span></a></h2><ul>
<li>可以将js代码编写到标签的onclick属性或超链接的href属性中（当我们点击按钮时，js代码才会执行</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;button onclick="alert('xxx');"&gt;button&lt;/button&gt;`</span></div></div><div><div><div>2</div></div><div><span>&lt;a href="javascript:alert('xxx');"&gt;&lt;/a&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;a href="javascript:;"&gt;&lt;/a&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>可以将js代码编写到script标签</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script type="text/javascript"&gt;alert("我是script标签中的代码");&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>可以将js代码编写到外部js文件中，然后通过script标签引入</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>&lt;script type="text/javascript" src="js/script.js"&gt;&lt;/script&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注：script标签一旦用于引入外部文件了，就不能在编写代码了，即使编写了浏览器也会忽略<br />
如果需要则可以在创建一个新的script标签用于编写内部代码</p></section><section><h2>字面量与变量<a href="#字面量与变量"><span>#</span></a></h2><section><h3>字面量<a href="#字面量"><span>#</span></a></h3><p>字面量（常量），都是一些不可改变的值<br />
比如 ：1 2 3 4 5<br />
字面量都是可以直接使用，但是我们一般都不会直接使用字面量</p></section><section><h3>变量<a href="#变量"><span>#</span></a></h3><p>变量可以用来保存字面量，而且变量的值是可以任意改变的<br />
变量更加方便我们使用，所以在开发中都是通过变量去保存一个字面量，而很少直接使用字面量，可以通过变量对字面量进行描述</p></section><section><h3>声明赋值<a href="#声明赋值"><span>#</span></a></h3><p>通过 <code>var</code> 来声明变量</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var = a</span></div></div><div><div><div>2</div></div><div><span>a = 100</span></div></div><div><div><div>3</div></div><div><span>var b = 100</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>标识符<a href="#标识符"><span>#</span></a></h2><p>在JS中所有的可以由我们自主命名的都可以称为是标识符</p><ul>
<li>
<p>例如：变量名、函数名、属性名都属于标识符</p>
</li>
<li>
<p>命名一个标识符时需要遵守如下的规则：
1.标识符中可以含有字母、数字、<code>_</code>、<code>$</code>
2.标识符不能以数字开头
3.标识符不能是ES中的关键字或保留字
4.标识符一般都采用驼峰命名法</p>
<ul>
<li>首字母小写，每个单词的开头字母大写，其余字母小写
<code>helloWorld xxxYyyZzz</code></li>
</ul>
</li>
<li>
<p>JS底层保存标识符时实际上是采用的Unicode编码，所以理论上讲，所有的utf-8中含有的内容都可以作为标识符</p>
</li>
</ul></section><section><h2>数据类型<a href="#数据类型"><span>#</span></a></h2><ul>
<li>JS中一共分成六种数据类型</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>String 字符串</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>Number 数值</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Boolean 布尔值</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>Null 空值</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>Undefined 未定义</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Object 对象</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3><strong>基本数据类型</strong><a href="#基本数据类型"><span>#</span></a></h3><section><h4><strong>String 字符串</strong><a href="#string-字符串"><span>#</span></a></h4><ul>
<li>JS中的字符串需要使用引号引起来双引号或单引号都行</li>
<li>在字符串中使用\作为转义字符</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>\'  ==&gt; '</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>\"  ==&gt; "</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>\n  ==&gt; 换行</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>\t  ==&gt; 制表符</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>\\  ==&gt; \</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>使用typeof运算符检查字符串时，会返回”string”</li>
</ul></section><section><h4>Number 数值<a href="#number-数值"><span>#</span></a></h4><ul>
<li>JS中所有的整数和浮点数都是Number类型</li>
<li>特殊的数字</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Infinity 正无穷</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>-Infinity 负无穷</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>NaN 非法数字（Not A Number）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>其他进制的数字的表示：</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>0b 开头表示二进制，但是不是所有的浏览器都支持</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>0 开头表示八进制</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>0x 开头表示十六进制</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>使用<code>typeof</code>检查一个Number类型的数据时，会返回<code>number</code>
（包括NaN 和 Infinity）</li>
</ul></section><section><h4>Boolean 布尔值<a href="#boolean-布尔值"><span>#</span></a></h4><ul>
<li>布尔值主要用来进行逻辑判断，布尔值只有两个</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>true 逻辑的真</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>false 逻辑的假</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>使用<code>typeof</code>检查一个布尔值时，会返回<code>boolean</code></li>
</ul></section><section><h4>Null 空<a href="#null-空"><span>#</span></a></h4><ul>
<li>空值专门用来表示为空的对象</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>Null类型的值只有一个 null</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>使用<code>typeof</code>检查一个Null类型的值时会返回<code>object</code>”</li>
</ul></section><section><h4>Undefined 未定义<a href="#undefined-未定义"><span>#</span></a></h4><ul>
<li>如果声明一个变量但是没有为变量赋值此时变量的值就是<code>undefined</code></li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>该类型的值只有一个 undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>
<p>使用<code>typeof</code>检查一个<code>Undefined</code>类型的值时，会返回<code>undefined</code></p>
</li>
<li>
<p><strong>注：</strong><code>typeof</code> 判断<code>null</code>与<code>array</code>都是返回<code>object</code>。可以用<code>instanceof</code>判断引用数据类型，如 <code>a instanceof Object</code> 返回一个<code>boolean</code>值</p>
</li>
</ul></section></section><section><h3><strong>引用数据类型</strong><a href="#引用数据类型"><span>#</span></a></h3><section><h4>Object 对象<a href="#object-对象"><span>#</span></a></h4></section></section></section><section><h2>运算符<a href="#运算符"><span>#</span></a></h2><ul>
<li>运算符也称为操作符</li>
<li>通过运算符可以对一个或多个值进行运算或操作</li>
</ul><section><h3>typeof运算符<a href="#typeof运算符"><span>#</span></a></h3><ul>
<li>用来检查一个变量的数据类型</li>
<li>语法：<code>typeof 变量</code></li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var a = 100</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>typeof a //number</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>它会返回一个用于描述类型的字符串作为结果</li>
</ul></section><section><h3>算数运算符<a href="#算数运算符"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>+  对两个值进行加法运算并返回结果</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>-   对两个值进行减法运算并返回结果</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>*  对两个值进行乘法运算并返回结果</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>/  对两个值进行除法运算并返回结果</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>%  对两个值进行取余运算并返回结果</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>除了加法以外，对非Number类型的值进行运算时，都会先转换为Number然后在做运算。</li>
<li>而做加法运算时，如果是两个字符串进行相加，则会做拼串操作，将两个字符连接为一个字符串。</li>
<li>任何值和字符串做加法，都会先转换为字符串，然后再拼串</li>
</ul></section><section><h3>一元运算符<a href="#一元运算符"><span>#</span></a></h3><ul>
<li>一元运算符只需要一个操作数</li>
</ul></section><section><h3>一元的 +<a href="#一元的-"><span>#</span></a></h3><ul>
<li>就是正号，不会对值产生任何影响，但是可以将一个非数字转换为数字</li>
<li>例子：</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var a = true</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a = +a</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>一元的 -<a href="#一元的--"><span>#</span></a></h3><ul>
<li>就是负号，可以对一个数字进行符号位取反</li>
<li>例子：</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>var a = 10</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a = -a;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>自增<a href="#自增"><span>#</span></a></h3><p>自增可以使变量在原值的基础上自增1，自增使用 ++</p><p>自增可以使用 前<code>++</code>（<code>++a</code>）后<code>++</code>(<code>a++</code>)，无论是<code>++a</code> 还是 <code>a++</code>都会立即使原变量自增1</p><p>不同的是<code>++a</code>和<code>a++</code>的值是不同的，
<code>++a</code>的值是变量的新值（自增后的值）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>a = a + 1</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a = a</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>a++</code>的值是变量的原值（自增前的值）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>a = a</span></div></div><div><div><div>2</div></div><div><span>a = a + 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>自减<a href="#自减"><span>#</span></a></h3><p>自减可以使变量在原值的基础上自减1，自减使用 <code>--</code>
自减可以使用 前<code>--</code>（<code>--a</code>）后<code>--</code>(<code>a--</code>)，无论是<code>--a</code> 还是 a—都会立即使原变量自减1</p><p>不同的是—a和a—的值是不同的，</p><p><code>--a</code>的值是变量的新值（自减后的值）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>a = a - 1</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a = a</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>a--</code>的值是变量的原值（自减前的值）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>a = a</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>a = a - 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>]]></content>
    </entry>
</feed>
