<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://slothvips.github.io/</id>
  <title type="text">PandaBox</title>
  <subtitle type="text">panda 的二进制 &amp; 生活</subtitle>
  <updated>2025-04-28T14:27:49.000Z</updated>
  <author><name>panda</name></author>
  <link rel="alternate" href="https://slothvips.github.io/"/>
  <link rel="self" href="https://slothvips.github.io/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.9</generator>
    <entry>
      <id>https://slothvips.github.io/posts/xun-lu-suan-fa/</id>
      <title type="text">寻路算法</title>
      <published>2025-04-28T14:27:49.000Z</published>
      <updated>2025-04-28T14:27:49.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/xun-lu-suan-fa/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>何为寻路算法<a href="#何为寻路算法"><span>#</span></a></h2><p>如果你在玩游戏，可能会遇到一些需要寻找路径的场景，比如在一个迷宫中找到从起点到终点的路径,找到这条最短路径的算法就是寻路算法.</p></section>
<section><h2>算法核心<a href="#算法核心"><span>#</span></a></h2><p>算法的核心是采用广度优先遍历所有可能的路径,直到找到目标点为止.在这个过程中,我们需要记录每个节点的父节点,以便在找到目标点后,可以通过父节点回溯到起点.</p><p>这个算法的一个比喻,你可以想象一滴水从迷宫中某个点开始向四面八方蔓延,最先碰到终点的就是最短路径.</p></section>
<section><h2>code<a href="#code"><span>#</span></a></h2><p>talk is cheap, show me the code.</p><div><figure><figcaption></figcaption><pre><code></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/ru-he-huo-qu-dom-jie-dian-shang-bang-ding-de-suo-you-shi-jian/</id>
      <title type="text">如何获取dom节点上绑定的所有事件</title>
      <published>2024-10-07T02:04:39.000Z</published>
      <updated>2024-10-07T02:04:39.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/ru-he-huo-qu-dom-jie-dian-shang-bang-ding-de-suo-you-shi-jian/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>如何获取一个 dom 节点上绑定的所有事件<a href="#如何获取一个-dom-节点上绑定的所有事件"><span>#</span></a></h1><p>没有需求,突发奇想,我们应该怎么获取一个 dom 节点上绑定的事件呢?查看了一下,好像没有原生相关的 api 接口来提供这样的功能.</p><section><h2>devtool<a href="#devtool"><span>#</span></a></h2><p>可以在浏览器控制台使用<code>getEventListeners</code>这个 api 来获取和查看一个节点绑定的所有事件.</p><p>形如下边的代码:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>getEventListeners</span><span><span>(</span><span>window</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>控制台就会输出这个几点绑定的所有事件列表.</p><p>但是这个 api 只在控制台提供,所以我们没办法在我们的应用中进行使用.那我们只能另辟蹊径.我们可以从绑定事件的入口着手,只要经过这些入口来注册的事件,我们就有办法进行收集管理了.我们可以通过重写这些入口来实现我们的需求.</p></section><section><h2>我们要重写哪些东西呢?<a href="#我们要重写哪些东西呢"><span>#</span></a></h2><p>首先我们要明确要重写哪些入口?这就要涉及到 DOM 的演进历史了,他们就是 <code>DOM0,DOM1,DOM2,DOM3</code>,我们就绑定事件的区别来谈一下他们之间的区别:</p><p><code>DOM0</code>和<code>DOM1</code>&lt;主要依赖于&gt; <code>onXX</code> 的属性来控制事件的监听和移除,你可以在标签上绑定和使用 <code>js</code>代码来绑定;这种方式最大的缺陷是会覆盖掉之前绑定的事件,然后就是不可配置事件触发的相关选项,比如是采用<code>冒泡</code>还是<code>捕获</code>等.</p><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>"box"</span><span> </span><span>onclick</span><span>=</span><span>"</span><span>alert</span><span>(</span><span>1</span><span>)</span><span>"</span><span>&gt;&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>box</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>"box"</span><span>);</span></div></div><div><div><div>4</div></div><div><span>  </span><span>// DOM0 事件绑定</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>box</span><span>.</span><span>onclick</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>"click"</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><p>删除事件就比较简单了,直接将这个属性置为<code>null</code>即可.</p><p>而<code>DOM2</code>和<code>DOM3</code>就算是一个大升级了!对事件的绑定和移除依赖于<code>addEventListener</code>和<code>removeEventListener</code>这两个 api,相信大家都比较熟悉了,这里就不过多展示了;通过这种方式绑定的事件,不会覆盖之前绑定的事件,而且由于有第三个参数的存在,我们可以对事件的触发方式做一些控制</p><p>所以,由于 DOM0 和 DOM1 级别的事件只是一个属性,那么我们可以得出我们需要重写的只有<code>addEventListener</code>和<code>removeEventListener</code>这两个 api,那么我们接下来开始动工.</p><section><h3>重写 addEventListener 和 removeEventListener<a href="#重写-addeventlistener-和-removeeventlistener"><span>#</span></a></h3><p>实现思路:</p><ul>
<li>我们会使用一个 WeakMap 来映射 dom 到事件列表的集合</li>
<li>当元素节点使用我们重写的<code>addEventListener</code>方法绑定事件时,我们就会<code>push</code>对应的事件到节点对应的事件列表中</li>
<li>当元素节点使用我们重写的<code>removeEventListener</code>方法移除事件时,我们就会从节点对应的事件列表中删除掉这个事件</li>
</ul><p>下边是实现:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 用来存储每个 DOM 元素的事件及其监听器</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>eventMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>WeakMap</span><span>();</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>// 判断options是否相等</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>optionsEquals</span><span> </span><span>=</span><span><span> (</span><span>o1</span><span>, </span><span>o2</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span><span> (</span><span>o1</span><span> </span></span><span>===</span><span><span> </span><span>o2</span><span>) </span></span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>o1</span><span> </span></span><span>!==</span><span> </span><span>"object"</span><span> </span><span>||</span><span><span> </span><span>o1</span><span> </span></span><span>===</span><span> </span><span>null</span><span> </span><span>||</span><span> </span><span>typeof</span><span><span> </span><span>o2</span><span> </span></span><span>!==</span><span> </span><span>"object"</span><span> </span><span>||</span><span><span> </span><span>o2</span><span> </span></span><span>===</span><span> </span><span>null</span><span>) </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>const</span><span> </span><span>keys1</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>keys</span><span><span>(</span><span>o1</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>const</span><span> </span><span>keys2</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>keys</span><span><span>(</span><span>o2</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><span>keys1</span><span>.</span></span><span>length</span><span> </span><span>!==</span><span><span> </span><span>keys2</span><span>.</span></span><span>length</span><span>) </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>return</span><span><span> </span><span>keys1</span><span>.</span></span><span>every</span><span><span>((</span><span>key</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>o1</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>o2</span><span>[</span><span>key</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>
</div></div><div><div><div>19</div></div><div><span>  </span><span>// 保存原始的 addEventListener 方法</span></div></div><div><div><div>20</div></div><div><span>  </span><span>const</span><span> </span><span>originalAddEventListener</span><span> </span><span>=</span><span> </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>addEventListener</span><span>;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>  </span><span>// 保存原始的 removeEventListener 方法</span></div></div><div><div><div>23</div></div><div><span>  </span><span>const</span><span> </span><span>originalRemoveEventListener</span><span> </span><span>=</span><span> </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>removeEventListener</span><span>;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>  </span><span>// 重写 addEventListener</span></div></div><div><div><div>26</div></div><div><span>  </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>addEventListener</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</span><span>) {</span></span></div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>eventMap</span><span>.</span></span><span>has</span><span>(</span><span>this</span><span>)) {</span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>eventMap</span><span>.</span><span>set</span><span>(</span><span>this</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>// 如果这个函数被注册过,就不再注册</span></div></div><div><div><div>32</div></div><div><span>    </span><span>if</span><span><span> (</span><span>eventMap</span><span>.</span></span><span>get</span><span>(</span><span>this</span><span>).</span><span>some</span><span><span>((</span><span>item</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>item</span><span>.</span></span><span>type</span><span> </span><span>===</span><span><span> </span><span>type</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>item</span><span>.</span></span><span>listener</span><span> </span><span>===</span><span><span> </span><span>listener</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>optionsEquals</span><span><span>(</span><span>item</span><span>.</span></span><span>options</span><span><span>, </span><span>options</span><span>))) {</span></span></div></div><div><div><div>33</div></div><div><span>      </span><span>return</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>
</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></span><span>eventMap</span><span>.</span><span>get</span><span>(</span><span>this</span><span>).</span><span>push</span><span><span>({ </span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</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>// 调用原始的 addEventListener 方法</span></div></div><div><div><div>40</div></div><div><span>    </span><span>return</span><span><span> </span><span>originalAddEventListener</span><span>.</span></span><span>call</span><span>(</span><span>this</span><span><span>, </span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</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>
</div></div><div><div><div>43</div></div><div><span>  </span><span>// 重写 removeEventListener</span></div></div><div><div><div>44</div></div><div><span>  </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>removeEventListener</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</span><span>) {</span></span></div></div><div><div><div>45</div></div><div><span>    </span><span>if</span><span><span> (</span><span>eventMap</span><span>.</span></span><span>has</span><span>(</span><span>this</span><span>)) {</span></div></div><div><div><div>46</div></div><div><span>      </span><span>const</span><span> </span><span>events</span><span> </span><span>=</span><span><span> </span><span>eventMap</span><span>.</span></span><span>get</span><span>(</span><span>this</span><span>);</span></div></div><div><div><div>47</div></div><div><span>      </span><span>// 查找并移除匹配的事件</span></div></div><div><div><div>48</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>events</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>        </span><span>if</span><span><span> (</span><span>events</span><span>[</span><span>i</span><span>].</span></span><span>type</span><span> </span><span>===</span><span><span> </span><span>type</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>events</span><span>[</span><span>i</span><span>].</span></span><span>listener</span><span> </span><span>===</span><span><span> </span><span>listener</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>optionsEquals</span><span><span>(</span><span>events</span><span>[</span><span>i</span><span>].</span></span><span>options</span><span><span>, </span><span>options</span><span>)) {</span></span></div></div><div><div><div>50</div></div><div><span><span>          </span></span><span>events</span><span>.</span><span>splice</span><span><span>(</span><span>i</span><span>, </span></span><span>1</span><span>); </span><span>// 移除事件</span></div></div><div><div><div>51</div></div><div><span>          </span><span>break</span><span>; </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>    </span></span><span>}</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>// 调用原始的 removeEventListener 方法</span></div></div><div><div><div>57</div></div><div><span>    </span><span>return</span><span><span> </span><span>originalRemoveEventListener</span><span>.</span></span><span>call</span><span>(</span><span>this</span><span><span>, </span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</span><span>);</span></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>
</div></div><div><div><div>60</div></div><div><span>  </span><span>// 提供一个获取元素上的所有事件的方法</span></div></div><div><div><div>61</div></div><div><span><span>  </span></span><span>window</span><span>.</span><span>getRegisteredEvents</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>element</span><span>) {</span></span></div></div><div><div><div>62</div></div><div><span>    </span><span>return</span><span><span> </span><span>eventMap</span><span>.</span></span><span>get</span><span><span>(</span><span>element</span><span>) </span></span><span>||</span><span> [];</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></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这样我们就能获取到一个节点上所有使用<code>addEventListener</code>注册的事件了</p></section><section><h3>DOM0 和 DOM1 事件的收集<a href="#dom0-和-dom1-事件的收集"><span>#</span></a></h3><p>由于 DOM0 和 DOM1 只是一个属性,那么我们就需要遍历事件列表来获取这些属性是不是为 null 就可以了,我们只需要完善一下我们的代码即可:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 用来存储每个 DOM 元素的事件及其监听器</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>eventMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>WeakMap</span><span>();</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>// 判断options是否相等</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>optionsEquals</span><span> </span><span>=</span><span><span> (</span><span>o1</span><span>, </span><span>o2</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span><span> (</span><span>o1</span><span> </span></span><span>===</span><span><span> </span><span>o2</span><span>) </span></span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>o1</span><span> </span></span><span>!==</span><span> </span><span>"object"</span><span> </span><span>||</span><span><span> </span><span>o1</span><span> </span></span><span>===</span><span> </span><span>null</span><span> </span><span>||</span><span> </span><span>typeof</span><span><span> </span><span>o2</span><span> </span></span><span>!==</span><span> </span><span>"object"</span><span> </span><span>||</span><span><span> </span><span>o2</span><span> </span></span><span>===</span><span> </span><span>null</span><span>) </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>const</span><span> </span><span>keys1</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>keys</span><span><span>(</span><span>o1</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>const</span><span> </span><span>keys2</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>keys</span><span><span>(</span><span>o2</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><span>keys1</span><span>.</span></span><span>length</span><span> </span><span>!==</span><span><span> </span><span>keys2</span><span>.</span></span><span>length</span><span>) </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>return</span><span><span> </span><span>keys1</span><span>.</span></span><span>every</span><span><span>((</span><span>key</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>o1</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>o2</span><span>[</span><span>key</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>
</div></div><div><div><div>19</div></div><div><span>  </span><span>// 保存原始的 addEventListener 方法</span></div></div><div><div><div>20</div></div><div><span>  </span><span>const</span><span> </span><span>originalAddEventListener</span><span> </span><span>=</span><span> </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>addEventListener</span><span>;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>  </span><span>// 保存原始的 removeEventListener 方法</span></div></div><div><div><div>23</div></div><div><span>  </span><span>const</span><span> </span><span>originalRemoveEventListener</span><span> </span><span>=</span><span> </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>removeEventListener</span><span>;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>  </span><span>// 重写 addEventListener</span></div></div><div><div><div>26</div></div><div><span>  </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>addEventListener</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</span><span>) {</span></span></div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>eventMap</span><span>.</span></span><span>has</span><span>(</span><span>this</span><span>)) {</span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>eventMap</span><span>.</span><span>set</span><span>(</span><span>this</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>// 如果这个函数被注册过,就不再注册</span></div></div><div><div><div>32</div></div><div><span>    </span><span>if</span><span><span> (</span><span>eventMap</span><span>.</span></span><span>get</span><span>(</span><span>this</span><span>).</span><span>some</span><span><span>((</span><span>item</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>item</span><span>.</span></span><span>type</span><span> </span><span>===</span><span><span> </span><span>type</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>item</span><span>.</span></span><span>listener</span><span> </span><span>===</span><span><span> </span><span>listener</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>optionsEquals</span><span><span>(</span><span>item</span><span>.</span></span><span>options</span><span><span>, </span><span>options</span><span>))) {</span></span></div></div><div><div><div>33</div></div><div><span>      </span><span>return</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>
</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></span><span>eventMap</span><span>.</span><span>get</span><span>(</span><span>this</span><span>).</span><span>push</span><span><span>({ </span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</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>// 调用原始的 addEventListener 方法</span></div></div><div><div><div>40</div></div><div><span>    </span><span>return</span><span><span> </span><span>originalAddEventListener</span><span>.</span></span><span>call</span><span>(</span><span>this</span><span><span>, </span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</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>
</div></div><div><div><div>43</div></div><div><span>  </span><span>// 重写 removeEventListener</span></div></div><div><div><div>44</div></div><div><span>  </span><span>EventTarget</span><span>.</span><span>prototype</span><span>.</span><span>removeEventListener</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</span><span>) {</span></span></div></div><div><div><div>45</div></div><div><span>    </span><span>if</span><span><span> (</span><span>eventMap</span><span>.</span></span><span>has</span><span>(</span><span>this</span><span>)) {</span></div></div><div><div><div>46</div></div><div><span>      </span><span>const</span><span> </span><span>events</span><span> </span><span>=</span><span><span> </span><span>eventMap</span><span>.</span></span><span>get</span><span>(</span><span>this</span><span>);</span></div></div><div><div><div>47</div></div><div><span>      </span><span>// 查找并移除匹配的事件</span></div></div><div><div><div>48</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>events</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>        </span><span>if</span><span><span> (</span><span>events</span><span>[</span><span>i</span><span>].</span></span><span>type</span><span> </span><span>===</span><span><span> </span><span>type</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>events</span><span>[</span><span>i</span><span>].</span></span><span>listener</span><span> </span><span>===</span><span><span> </span><span>listener</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>optionsEquals</span><span><span>(</span><span>events</span><span>[</span><span>i</span><span>].</span></span><span>options</span><span><span>, </span><span>options</span><span>)) {</span></span></div></div><div><div><div>50</div></div><div><span><span>          </span></span><span>events</span><span>.</span><span>splice</span><span><span>(</span><span>i</span><span>, </span></span><span>1</span><span>); </span><span>// 移除事件</span></div></div><div><div><div>51</div></div><div><span>          </span><span>break</span><span>; </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>    </span></span><span>}</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>// 调用原始的 removeEventListener 方法</span></div></div><div><div><div>57</div></div><div><span>    </span><span>return</span><span><span> </span><span>originalRemoveEventListener</span><span>.</span></span><span>call</span><span>(</span><span>this</span><span><span>, </span><span>type</span><span>, </span><span>listener</span><span>, </span><span>options</span><span>);</span></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>
</div></div><div><div><div>60</div></div><div><span>  </span><span>// 获取DOM0 和 DOM1 事件</span></div></div><div><div><div>61</div></div><div><span>  </span><span>function</span><span> </span><span>getDOM0AndDOM1Events</span><span><span>(</span><span>element</span><span>) {</span></span></div></div><div><div><div>62</div></div><div><span>    </span><span>const</span><span> </span><span>events</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>63</div></div><div><span>    </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>key</span><span> </span></span><span>in</span><span><span> </span><span>element</span><span>) {</span></span></div></div><div><div><div>64</div></div><div><span>      </span><span>const</span><span> </span><span>isFN</span><span> </span><span>=</span><span> </span><span>typeof</span><span><span> </span><span>element</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span> </span><span>"function"</span><span>;</span></div></div><div><div><div>65</div></div><div><span>      </span><span>if</span><span><span> (</span><span>key</span><span>.</span></span><span>startsWith</span><span>(</span><span>"on"</span><span>) </span><span>&amp;&amp;</span><span><span> </span><span>isFN</span><span>) {</span></span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>events</span><span>.</span><span>push</span><span>({</span></div></div><div><div><div>67</div></div><div><span>          </span><span>type</span><span>:</span><span><span> </span><span>key</span><span>.</span></span><span>slice</span><span>(</span><span>2</span><span>).</span><span>toLowerCase</span><span>(),</span></div></div><div><div><div>68</div></div><div><span>          </span><span>listener</span><span>:</span><span><span> </span><span>element</span><span>[</span><span>key</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>      </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><span>return</span><span><span> </span><span>events</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>
</div></div><div><div><div>75</div></div><div><span>  </span><span>// 提供一个获取元素上的所有事件的方法</span></div></div><div><div><div>76</div></div><div><span><span>  </span></span><span>window</span><span>.</span><span>getRegisteredEvents</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>element</span><span>) {</span></span></div></div><div><div><div>77</div></div><div><span>    </span><span>return</span><span> [</span><span>...</span><span><span>(</span><span>eventMap</span><span>.</span></span><span>get</span><span><span>(</span><span>element</span><span>) </span></span><span>||</span><span> []), </span><span>...</span><span>getDOM0AndDOM1Events</span><span><span>(</span><span>element</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></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>我们补充了<code>getDOM0AndDOM1Events</code>这个函数来获取<code>DOM0</code>和<code>DOM1</code>的事件,这样这个函数就算大功告成了~</p></section></section><section><h2>总结<a href="#总结"><span>#</span></a></h2><p>我们使用一下三步来实现了我们的需求:</p><ol>
<li>
<p>重写 addEventListener 和 removeEventListener 方法：通过重写这两个方法，我们可以追踪所有通过 DOM2 级事件绑定的事件。</p>
</li>
<li>
<p>获取 DOM0 和 DOM1 级事件：我们编写了 getDOM0AndDOM1Events 函数来获取这些较早的事件绑定方式。</p>
</li>
<li>
<p>提供全局方法 getRegisteredEvents：这个方法结合了上述所有技术，可以获取一个元素的所有绑定事件。</p>
</li>
</ol><p>需要保证这个函数优先执行,避免我们还没有重写 addEventListener 和 removeEventListener 方法之前就已经注册了事件.
ok,bye~</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/guo-qi-de-fu-zuo-yong/</id>
      <title type="text">过期的副作用</title>
      <published>2024-04-03T20:13:34.000Z</published>
      <updated>2024-04-03T20:13:34.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/guo-qi-de-fu-zuo-yong/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>起因<a href="#起因"><span>#</span></a></h2><p>今天在工作中遇到一个问题,可以想象这样一个场景&lt;你会连续发送两次相同的请求&gt;,你期望总是以最后发起的请求为最终结果,但是有时候发现结果不尽人意,仔细观察之后发现,这是由于有时候请求并不是按照你依次发送的时间来响应的,有时候会出现第一次请求的响应比第二次慢,那么就会出现错误的数据覆盖,我们用代码来模拟一下这个场景:</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>const</span><span> </span><span>req</span><span> </span><span>=</span><span><span> (</span><span>q</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>random</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>random</span><span>() </span><span>*</span><span> </span><span>10</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>resolve</span><span><span>(</span><span>q</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}, </span><span>random</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>let</span><span><span> </span><span>msg</span><span> </span></span><span>=</span><span> </span><span>""</span><span>;</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>p1</span><span> </span><span>=</span><span> </span><span>req</span><span>(</span><span>1</span><span>).</span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>msg</span><span> </span><span>=</span><span><span> </span><span>res</span><span>;</span></span></div></div><div><div><div>13</div></div><div><span>});</span></div></div><div><div><div>14</div></div><div><span>const</span><span> </span><span>p2</span><span> </span><span>=</span><span> </span><span>req</span><span>(</span><span>2</span><span>).</span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>msg</span><span> </span><span>=</span><span><span> </span><span>res</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>Promise</span><span>.</span><span>all</span><span><span>([</span><span>p1</span><span>, </span><span>p2</span><span>]).</span></span><span>then</span><span>(() </span><span>=&gt;</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><span>msg</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>req</code>函数会随机时间后再解决 promise,这正是我们想要的,我们借用这个函数模拟一次网络请求;接着我们接连发送了两次请求,等两次请求全部响应之后,查看<code>msg</code>的值,会发现并不是每次都获取到了一样的结果,这完美复现了我们上边的提到的问题.</p><p>那么我们接下来尝试解决这个问题</p></section>
<section><h2>解决方案一&lt;缓存&gt;<a href="#解决方案一"><span>#</span></a></h2><p>我们可以借助缓存来解决,每次请求结束之后,我们从缓存中获取最靠后请求的结果,那么我们总是能得到正确的结果:</p><p>上代码:</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>const</span><span> </span><span>cache</span><span> </span><span>=</span><span> {};</span></div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>id</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>getLatestReqKey</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>keys</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>keys</span><span><span>(</span><span>cache</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>keys</span><span>.</span></span><span>length</span><span>) </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div><span>  </span><span>return</span><span><span> </span><span>Math</span><span>.</span></span><span>max</span><span>(</span><span>...</span><span><span>keys</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>const</span><span> </span><span>cacheReq</span><span> </span><span>=</span><span><span> (</span><span>q</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> </span><span>c_id</span><span> </span><span>=</span><span> </span><span>++</span><span><span>id</span><span>;</span></span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> </span><span>req</span><span><span>(</span><span>q</span><span>).</span></span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span><span> (</span><span>c_id</span><span> </span></span><span>&gt;</span><span> </span><span>+</span><span>getLatestReqKey</span><span>()) {</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>cache</span><span>[</span><span>c_id</span><span>] </span><span>=</span><span><span> </span><span>res</span><span>;</span></span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>msg</span><span> </span><span>=</span><span><span> </span><span>res</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></div></div><div><div><div>18</div></div><div><span>const</span><span> </span><span>p1_c</span><span> </span><span>=</span><span> </span><span>cacheReq</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>19</div></div><div><span>const</span><span> </span><span>p2_c</span><span> </span><span>=</span><span> </span><span>cacheReq</span><span>(</span><span>2</span><span>);</span></div></div><div><div><div>20</div></div><div><span>Promise</span><span>.</span><span>all</span><span><span>([</span><span>p1_c</span><span>, </span><span>p2_c</span><span>]).</span></span><span>then</span><span>(() </span><span>=&gt;</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><span>msg</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><h2>解决方案二&lt;标记过期函数&gt;<a href="#解决方案二"><span>#</span></a></h2><p>这里借鉴了 vue3<code>watch</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>let</span><span><span> </span><span>cleanup</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>reqExpired</span><span> </span><span>=</span><span><span> (</span><span>q</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>let</span><span><span> </span><span>expired</span><span> </span></span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>cleanup</span><span> </span><span>&amp;&amp;</span><span> </span><span>cleanup</span><span>();</span></div></div><div><div><div>6</div></div><div><span>  </span><span>cleanup</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>expired</span><span> </span><span>=</span><span> </span><span>true</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>req</span><span><span>(</span><span>q</span><span>).</span></span><span>then</span><span><span>((</span><span>res</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>!</span><span><span>expired</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>msg</span><span> </span><span>=</span><span><span> </span><span>res</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><span>};</span></div></div><div><div><div>15</div></div><div><span>const</span><span> </span><span>p1_e</span><span> </span><span>=</span><span> </span><span>reqExpired</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>16</div></div><div><span>const</span><span> </span><span>p2_e</span><span> </span><span>=</span><span> </span><span>reqExpired</span><span>(</span><span>2</span><span>);</span></div></div><div><div><div>17</div></div><div><span>Promise</span><span>.</span><span>all</span><span><span>([</span><span>p1_e</span><span>, </span><span>p2_e</span><span>]).</span></span><span>then</span><span>(() </span><span>=&gt;</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>msg</span><span>);</span></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></section>
<section><h2>题外话&lt;令牌取消思想&gt;<a href="#题外话"><span>#</span></a></h2><p>众所周知,<code>promise</code>是无法取消的,但是我们可以借助令牌取消思想,让其永远停在<code>pendding</code>状态,也相当于是一种取消了.下边贴一段红宝书的代码:</p><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>"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>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>6</div></div><div><span><span>    </span></span><span>&lt;</span><span>title</span><span>&gt;Document&lt;/</span><span>title</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;/</span><span>head</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;</span><span>body</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>id</span><span>=</span><span>"start"</span><span>&gt;start&lt;/</span><span>button</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>id</span><span>=</span><span>"cancel"</span><span>&gt;cancel&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>script</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span>      </span><span>class</span><span> </span><span>CancelToken</span><span> {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>constructor</span><span><span>(</span><span>cancelFn</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>          </span><span>this</span><span>.</span><span>promise</span><span> </span><span>=</span><span> </span><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>15</div></div><div><span>            </span><span>cancelFn</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span>              </span><span>setTimeout</span><span><span>(</span><span>console</span><span>.</span></span><span>log</span><span>, </span><span>0</span><span>, </span><span>"delay cancelled"</span><span>);</span></div></div><div><div><div>17</div></div><div><span>              </span><span>resolve</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>const</span><span> </span><span>startButton</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelector</span><span>(</span><span>"#start"</span><span>);</span></div></div><div><div><div>23</div></div><div><span>      </span><span>const</span><span> </span><span>cancelButton</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelector</span><span>(</span><span>"#cancel"</span><span>);</span></div></div><div><div><div>24</div></div><div><span>      </span><span>function</span><span> </span><span>cancellableDelayedResolve</span><span><span>(</span><span>delay</span><span>) {</span></span></div></div><div><div><div>25</div></div><div><span>        </span><span>setTimeout</span><span><span>(</span><span>console</span><span>.</span></span><span>log</span><span>, </span><span>0</span><span>, </span><span>"set delay"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>        </span><span>return</span><span> </span><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>27</div></div><div><span>          </span><span>let</span><span><span> </span><span>flag</span><span> </span></span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>          </span><span>req</span><span>().</span><span>then</span><span><span>((</span><span>res</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>30</div></div><div><span>if</span><span><span>(</span><span>flag</span><span>){</span></span></div></div><div><div><div>31</div></div><div><span>            </span><span>resolve</span><span>();</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>}</span></div></div><div><div><div>34</div></div><div>
</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>const</span><span> </span><span>id</span><span> </span><span>=</span><span> </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>37</div></div><div><span>            </span><span>setTimeout</span><span><span>(</span><span>console</span><span>.</span></span><span>log</span><span>, </span><span>0</span><span>, </span><span>"delayed resolve"</span><span>);</span></div></div><div><div><div>38</div></div><div><span><span>          </span></span><span>}, </span><span>delay</span><span>);</span></div></div><div><div><div>39</div></div><div><span>          </span><span>const</span><span> </span><span>cancelToken</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>CancelToken</span><span><span>((</span><span>cancelCallback</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>cancelButton</span><span>.</span></span><span>addEventListener</span><span>(</span><span>"click"</span><span><span>, </span><span>cancelCallback</span><span>));</span></span></div></div><div><div><div>40</div></div><div><span><span>          </span></span><span>cancelToken</span><span>.</span><span>promise</span><span>.</span><span>then</span><span>(() </span><span>=&gt;</span><span><span> (</span><span>flag</span><span> </span></span><span>=</span><span> </span><span>true</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>}</span></div></div><div><div><div>43</div></div><div><span><span>      </span></span><span>startButton</span><span>.</span><span>addEventListener</span><span>(</span><span>"click"</span><span>, () </span><span>=&gt;</span><span> </span><span>cancellableDelayedResolve</span><span>(</span><span>1000</span><span>));</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>&lt;/</span><span>body</span><span>&gt;</span></div></div><div><div><div>46</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><p>当我们每次点击 start 的时候,我们就会创建一个 promise,并在其执行器中实例化一个 CancelToken,当我们点击 cancel 的时候,这个 CancelToken 实例对象上的 promise 就会被立即解决,那么我们就可以借此时机来执行我们想达到的取消操作!</p></section>
<section><h2>结束<a href="#结束"><span>#</span></a></h2><p>bye~</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/du-zui-zhong-yao-de-shi-qing-zhi-you-yi-jian/</id>
      <title type="text">读&lt;&lt;最重要的事情只有一件&gt;&gt;</title>
      <published>2024-03-17T18:40:35.000Z</published>
      <updated>2024-03-17T18:40:35.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/du-zui-zhong-yao-de-shi-qing-zhi-you-yi-jian/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>读&lt;&lt;最重要的事只有一件&gt;&gt;摘抄<a href="#读最重要的事只有一件摘抄"><span>#</span></a></h2><p>其实一个人做成的事情不在多，只要把事情做好了，就是成功。(panda&lt;但是&gt;,有时候环境希望你做更多的事情,所以很无奈.)</p><p>坚持一个目标才能成功。(panda&lt;坚持&gt;,即使面对再多的诱惑,面对再多的选择,你都应该坚持.)</p><p>生活中的芝麻小事永远不应阻挡你去追逐伟大的事。(panda&lt;这是我觉得最重要的事情&gt;,因为我本身是很容易被生活中各种小事垃圾情绪影响.明白这个道理,即使当时偏离航道,但是你最终也不会迷失方向,始终会回到正确的道路上.)</p><ol>
<li>缩减。不要纠缠在烦乱的忙碌之中，要执着于效率。你每天的工作都应围绕着终极目标而展开。</li>
<li>极致。一旦你意识到什么是重要的，就要继续追问更重要的是什么，直到找到那件最重要的事。将主要精力放在成功清单的第一项上。</li>
<li>拒绝。为紧要的事腾出时间，对其他事情暂时说“不”或者拖延一阵。</li>
<li>别掉进“待处理事项”的陷阱。区别对待每件事，而且要真正做到。千万不要认为所有的事情都需要做完才行，也不要相信“要成功就得完成所有目标”这样的观点。别掉进“待处理事项”的陷阱，划掉清单上的琐事绝不能助你成功。每件事都不同，选择最重要的事才会有所裨益。</li>
</ol><p>调查研究显示，员工每11分钟就被打断一次，他们每天有1/3的时间花在从干扰中恢复的过程上。
1.大脑的潜能是无限的。要分区充分利用，也要做好浪费时间和降低效率的准备。 2.切换至另一项任务花费的时间越多，你就越难回到原始任务上，这样虎头蛇尾的事只会越积越多。 3.在不同的任务之间来回周旋将耗费时间，这些零碎时间会积少成多。研究显示，我们在一个工作日内平均有28%的时间浪费在任务转换上。 4.习惯性“多面手”无法准确地估计完成一项工作的确切时间，他们的预计往往比实际耗时长。 5.“多面手”更容易犯错。他们总轻信新信息，却对更有价值的旧信息视而不见。 6.“多面手”的私人时间较少，忧虑和压力较多。</p><p>哪些因素会消耗你的意志力 ·培养新习惯 ·考试 ·屏蔽干扰 ·尝试引起他人的注意 ·抵制诱惑 ·战胜恐惧 ·压抑情感 ·做不喜欢的事 ·抑制侵犯 ·在长期回报和短期利益之间做选择 ·防止冲动 ·选择长期或短期奖励</p><ol>
<li>别让你的意志力太分散。珍惜每天有限的意志力，找出最重要的事再充分利用。</li>
<li>注意饮食。若要意志力满格，首先要保证能量满格，别因为大脑供能不足就妥协。健康饮食，规律进餐。</li>
<li>安排任务处理时间。在每天意志力最强的时候做最重要的事。意志力越强，成功越有保障。</li>
</ol><p>你若在工作和生活中都摆出较高的姿态，要求颇多，就会让人十分崩溃，那时我们更会哀号：“我没有自己的生活！”(panda&lt;所以一定要放低自己的姿态&gt;,不仅仅别人得到了满足,自己也更容易满足!)</p><p>想要取得非凡的成就，你就必须在繁杂事物中去粗取精，选择最重要的事情、并为之付出足够的时间。(取舍,取舍,再取舍.)</p><p>如果把生活想象成一场五球杂耍游戏，这五个球分别是工作、家庭、健康、朋友和诚信。你把球抛到空中，游戏就开始了。有一天，你会发现工作是一个橡皮球，如果掉到地上，它会弹回来；而其他的四个球——家庭、健康、朋友、诚信——都是玻璃做的，一旦掉了下来，就会磨损、破裂甚至摔个粉碎，到时再后悔已来不及了</p><p>当我们把“大”和“坏”联系在一起的时候，就会导致畏首畏尾的思考。降低目标能使我们感到安全，待在舒适区看上去是一种谨慎的做法。但是，其消极的一面也是显而易见的——当“大”被认为不好时，各类小事就霸占了我们的时间，因此远大的理想就永远见不到曙光。</p><p>当人们谈到“重建”自己的职业生涯时，小“盒子”往往是其深刻的根源。你现在获得的成功将使你更强大，或者会让你受到限制 。它要么成为你下一阶段成功的平台，要么就会将你困入其中。</p><p>你不要畏惧志存高远，而是要恐惧平庸、恐惧浪费、恐惧没有全力以赴地活过。</p><ol>
<li>
<p>志存高远。 停止追问自己：“接下来我要做什么？”这至多是取得成功的慢车道，甚至是出口匝道。你要问自己更宏大的问题。一条很棒的经验法则，就是在你生活中的每一处都双倍下注。如果你的目标是10，那么就要问自己：“我怎样才能达到20？”为自己制定一个远超过自己期待的目标，那么你将做出能保障你原本目标的计划。</p>
</li>
<li>
<p>不要按照菜单点菜。 苹果公司在1997年有一个很出名的广告——“非同凡‘想’”，其中有很多著名人物，例如拳王阿里、鲍伯·迪伦、爱因斯坦、希区柯克、毕加索、甘地，以及其他一些名人。他们都从不同的角度看待问题，从而改变了这个世界。重点在于他们从不选择已提供的选项，而是创造出了史无前例的结果。正如该广告提醒我们的一样：“有些人非常疯狂，认为自己能够改变这个世界，他们也确实做到了。”</p>
</li>
<li>
<p>大胆行动。 没有大胆的行动，伟大的设想就不会有任何结果。一旦你提出了一个大问题，就要停下来想象一下：如果找不到答案，生活会是什么祥？你可以向那些已经找到答案的人学习，看看他们的模式、体系、习惯和人际关系是怎样的。我们都认为自己是与众不同的，因此那些对别人有用的因素通常也适用于我们。</p>
</li>
<li>
<p>不要恐惧失败。 恐惧是通向成功的必经之路。拥有发展的思维模式，不要担心你的未来。非凡的成就不仅建立在非凡的成功之上，它们也建立在失败之上。更准确地说，成功必然要建立在失败之上。失败时，我们就会停下来，问问自己需要做什么才能取得成功。你要在错误中不断学习，不断成长。不要害怕失败，你应将失败看作学习过程中的一部分，坚持不懈地为发掘自己的真正潜力而奋斗。</p>
</li>
</ol><p>首先，我不再咬紧牙关、握紧拳头、挺胸收腹、屏住呼吸了，我开始倾听自己的身体，放慢节奏，使自己冷静下来。然后，我穿着T恤衫和牛仔裤上班，鼓励所有人对我的穿着做出评价。我不再说成功人士的那套话，不再以胜者的姿态待人，只是做我自己。我和家人一起吃早饭，我开始健身，并且保持着健康的精神状态。最后，我做得越来越少，真的很少，而且是故意这么做的。我比以前轻松多了，前面的路变得更宽阔了，我又能自由呼吸了。我向成功的思维定势发出挑战，结果是我变得更成功了——这完全是我不曾想到的，我感觉生活比以往任何时候都美好。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/liao-yi-liao-promise/</id>
      <title type="text">聊一聊Promise</title>
      <published>2023-12-15T20:53:34.000Z</published>
      <updated>2023-12-15T20:53:34.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/liao-yi-liao-promise/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>Hello,大家好,我们今天来聊一聊 js 中的<code>Promise</code>,希望能对它有进一步的认识.</p>
<section><h2>它出现的原因是什么?<a href="#它出现的原因是什么"><span>#</span></a></h2><p>一个技术的出现一定是为了解决某一个问题或痛点,promise 也是这样的背景下出现的,它的出现将我们从异步的回调地狱中解脱了出来!
在 promise 出现之前,如果你想在这个异步任务完成之后,获取异步的任务的结果以及做一些后续的操作,那么我们书写异步任务的代码一般会是下边这个样子</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>const</span><span> </span><span>req1</span><span> </span><span>=</span><span><span> (</span><span>callback</span><span>) </span></span><span>=&gt;</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>callback</span><span>(</span><span>"done"</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></div></div><div><div><div>7</div></div><div><span>req1</span><span><span>(</span><span>console</span><span>.</span></span><span>log</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样看似也没有什么问题.但是如果你有一堆的异步任务,而你的程序需要这些异步任务按照一个特定的顺序执行,那么问题就出现了.</p><p>这样的情况下,多次采用异步回调的方式就会形成非常经典的嵌套地狱 😝
</p><figure><img alt="回调地狱" loading="lazy" width="1066" height="602" src="/_astro/%E5%9B%9E%E8%B0%83%E5%9C%B0%E7%8B%B1.BTjnkJ33_ZxRz8r.webp" /><figcaption>回调地狱</figcaption></figure><p></p><p>而随着 es6 的出现,js 获得了原生级别的 promise 支持,改变了我们书写异步代码的方式,让我们从嵌套中得到了解脱.</p></section>
<section><h2>如何使用 promise 来解决嵌套呢?<a href="#如何使用-promise-来解决嵌套呢"><span>#</span></a></h2><p>如果我们用 promise 来包装我们的任务,那么就不会出现回调地狱的问题了,我们来看一下代码:</p><div><div><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><span>const</span><span> </span><span>req1P</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>req1</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>      </span><span>resolve</span><span>(</span><span>"req1"</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>const</span><span> </span><span>req2P</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>req2</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>      </span><span>resolve</span><span>(</span><span>"req2"</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></div></div><div><div><div>16</div></div><div><span>const</span><span> </span><span>req3P</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>req3</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>resolve</span><span>(</span><span>"req3"</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><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>req1P</span><span>()</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>.</span><span>then</span><span><span>((</span><span>msg1</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>req2P</span><span>().</span><span>then</span><span><span>((</span><span>msg2</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span>      </span><span>return</span><span><span> [</span><span>msg1</span><span>, </span><span>msg2</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></span><span>})</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>.</span><span>then</span><span><span>((</span><span>msgs</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>return</span><span> </span><span>req3P</span><span>().</span><span>then</span><span><span>((</span><span>msg3</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>32</div></div><div><span>      </span><span>return</span><span> [</span><span>...</span><span><span>msgs</span><span>, </span><span>msg3</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><span>then</span><span><span>((</span><span>msgs</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>msgs</span><span>);</span></span></div></div><div><div><div>37</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><p>即使社区做了这么多努力改善我们的异步代码编写体验,我们还是会在一些地方看到有些奇怪的 promise 使用方式,比如下边这样的使用方式</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><span>req1P</span><span>().</span><span>then</span><span><span>((</span><span>msg1</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>req2P</span><span>().</span><span>then</span><span><span>((</span><span>msg2</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>req3P</span><span>().</span><span>then</span><span><span>((</span><span>msg3</span><span>) </span></span><span>=&gt;</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></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>msg1</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>msg2</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>msg3</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></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它再次将 promise 打回了地狱 😀</p></section>
<section><h2>为了理解 Promise 的原理,我们来手写实现一个 Promise<a href="#为了理解-promise-的原理我们来手写实现一个-promise"><span>#</span></a></h2><p>废话说了这么多,终于到重点了.我们需要自己手写一版的 promise 来彻底的理解它.接下里我们会根据<code>promiseA+</code>规范来一步一步实现一个完整的 promise,那么我们首先要了解一下什么是 promiseA+规范.</p><p>可以通过下边的链接来一睹规范详情:
<a href="https://promisesaplus.com/" target="_blank">promiseA+规范</a></p><p>文档是英文的,这里也有中文的翻译版本 😀:
<a href="https://www.ituring.com.cn/article/66566" target="_blank">promiseA+规范中文翻译</a></p><p>好的,有了规范内容,我们接下来就按照规范一步一步来实现一版我们自己的 promise 吧~</p><p>实现回大致分两步:</p><ul>
<li>第一步先实现一个基本的 promise 实现,这部分基本上没什么难度,大致说一下整个运行的流程,结合平时对 promise 的使用不难理解这个过程。</li>
<li>第二步我们将实现 promise 的链式调用,以及规范中定义的 promise 的解决过程,我们到时候会专门书写一个函数<code>resolvePromise</code>来实现这个解决过程.</li>
</ul><p>这一块建议配合视频食用,这里就不一步一步去讲解了.</p><p><a href="https://www.douyin.com/user/self?from_tab_name=main&amp;modal_id=7321772418794540303&amp;showTab=post" target="_blank">Promise 实现全过程</a></p><p>可以看到在视频的最后,我们通过了全部的测试用例~,<code>我穿山甲的任务完成啦</code>~</p><p>完整代码:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>queueTask</span><span><span>(</span><span>fn</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>setTimeout</span><span><span>(</span><span>fn</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>class</span><span> </span><span>PdPromise</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>static</span><span><span> </span><span>PENDING</span><span> </span></span><span>=</span><span> </span><span>"pending"</span><span>;</span></div></div><div><div><div>6</div></div><div><span>  </span><span>static</span><span><span> </span><span>FULFILLED</span><span> </span></span><span>=</span><span> </span><span>"fulfilled"</span><span>;</span></div></div><div><div><div>7</div></div><div><span>  </span><span>static</span><span><span> </span><span>REJECTED</span><span> </span></span><span>=</span><span> </span><span>"rejected"</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>#status</span><span> </span><span>=</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>PENDING</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>#value</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>#reason</span><span> </span><span>=</span><span> </span><span>null</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>#fulfilledCallbacks</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>#rejectedCallbacks</span><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>constructor</span><span><span>(</span><span>executor</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>try</span><span> {</span></div></div><div><div><div>18</div></div><div><span>      </span><span>executor</span><span>(</span><span>this</span><span>.</span><span>resolve</span><span>, </span><span>this</span><span>.</span><span>reject</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>      </span><span>this</span><span>.</span><span>reject</span><span><span>(</span><span>e</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>resolve</span><span> </span><span>=</span><span><span> (</span><span>value</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>24</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>#status</span><span> </span><span>!==</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>PENDING</span><span>) </span><span>return</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></span><span>* 如果value是一个promise,那么这个Promise实例会根据value这个Promise的解决时机进行reslove或者reject</span></div></div><div><div><div>27</div></div><div><span><span>     </span></span><span>* 此处逻辑并不在规范之内,但是原生的Promise的行为是这样的,所以我们加上</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>if</span><span><span> (</span><span>value</span><span> </span></span><span>instanceof</span><span> </span><span>PdPromise</span><span>) {</span></div></div><div><div><div>30</div></div><div><span>      </span><span>return</span><span><span> </span><span>value</span><span>.</span></span><span>then</span><span>(</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>(</span><span>value</span><span>) </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>resolve</span><span><span>(</span><span>value</span><span>),</span></span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>(</span><span>reason</span><span>) </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>reject</span><span><span>(</span><span>reason</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>this</span><span>.</span><span>#status</span><span> </span><span>=</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>FULFILLED</span><span>;</span></div></div><div><div><div>36</div></div><div><span>    </span><span>this</span><span>.</span><span>#value</span><span> </span><span>=</span><span><span> </span><span>value</span><span>;</span></span></div></div><div><div><div>37</div></div><div><span>    </span><span>this</span><span>.</span><span>#fulfilledCallbacks</span><span>.</span><span>forEach</span><span><span>((</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> </span><span>fn</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>reject</span><span> </span><span>=</span><span><span> (</span><span>reason</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>40</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>#status</span><span> </span><span>!==</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>PENDING</span><span>) </span><span>return</span><span>;</span></div></div><div><div><div>41</div></div><div><span>    </span><span>this</span><span>.</span><span>#status</span><span> </span><span>=</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>REJECTED</span><span>;</span></div></div><div><div><div>42</div></div><div><span>    </span><span>this</span><span>.</span><span>#reason</span><span> </span><span>=</span><span><span> </span><span>reason</span><span>;</span></span></div></div><div><div><div>43</div></div><div><span>    </span><span>this</span><span>.</span><span>#rejectedCallbacks</span><span>.</span><span>forEach</span><span><span>((</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> </span><span>fn</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>then</span><span> </span><span>=</span><span><span> (</span><span>onfulfilled</span><span>, </span><span>onrejected</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>46</div></div><div><span>    </span><span>const</span><span> </span><span>onfulfilledBak</span><span> </span><span>=</span><span><span> </span><span>onfulfilled</span><span>;</span></span></div></div><div><div><div>47</div></div><div><span>    </span><span>const</span><span> </span><span>onrejectedBak</span><span> </span><span>=</span><span><span> </span><span>onrejected</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>onfulfilled</span><span> </span><span>=</span><span> </span><span>typeof</span><span><span> </span><span>onfulfilled</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span> </span><span>?</span><span><span> </span><span>onfulfilled</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>#value</span><span>;</span></div></div><div><div><div>50</div></div><div><span><span>    </span></span><span>onrejected</span><span> </span><span>=</span><span> </span><span>typeof</span><span><span> </span><span>onrejected</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span> </span><span>?</span><span><span> </span><span>onrejected</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>#reason</span><span>;</span></div></div><div><div><div>51</div></div><div><span>    </span><span>const</span><span> </span><span>promise</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>PdPromise</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>52</div></div><div><span>      </span><span>const</span><span> </span><span>resolveValue</span><span> </span><span>=</span><span><span> (</span><span>value</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>53</div></div><div><span>        </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>onfulfilledBak</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span>) {</span></div></div><div><div><div>54</div></div><div><span>          </span><span>const</span><span> </span><span>x</span><span> </span><span>=</span><span> </span><span>onfulfilled</span><span><span>(</span><span>value</span><span>);</span></span></div></div><div><div><div>55</div></div><div><span>          </span><span>resolvePromise</span><span><span>(</span><span>promise</span><span>, </span><span>x</span><span>, </span><span>resolve</span><span>, </span><span>reject</span><span>);</span></span></div></div><div><div><div>56</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>57</div></div><div><span>          </span><span>resolve</span><span><span>(</span><span>value</span><span>);</span></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>
</div></div><div><div><div>61</div></div><div><span>      </span><span>if</span><span> (</span><span>this</span><span>.</span><span>#status</span><span> </span><span>===</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>FULFILLED</span><span>) {</span></div></div><div><div><div>62</div></div><div><span>        </span><span>queueTask</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>63</div></div><div><span>          </span><span>try</span><span> {</span></div></div><div><div><div>64</div></div><div><span>            </span><span>resolveValue</span><span>(</span><span>this</span><span>.</span><span>#value</span><span>);</span></div></div><div><div><div>65</div></div><div><span><span>          </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>66</div></div><div><span>            </span><span>reject</span><span><span>(</span><span>e</span><span>);</span></span></div></div><div><div><div>67</div></div><div><span><span>          </span></span><span>}</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><span>if</span><span> (</span><span>this</span><span>.</span><span>#status</span><span> </span><span>===</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>REJECTED</span><span>) {</span></div></div><div><div><div>71</div></div><div><span>        </span><span>queueTask</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>72</div></div><div><span>          </span><span>try</span><span> {</span></div></div><div><div><div>73</div></div><div><span>            </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>onrejectedBak</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span>) {</span></div></div><div><div><div>74</div></div><div><span>              </span><span>const</span><span> </span><span>x</span><span> </span><span>=</span><span> </span><span>onrejected</span><span>(</span><span>this</span><span>.</span><span>#reason</span><span>);</span></div></div><div><div><div>75</div></div><div><span>              </span><span>resolvePromise</span><span><span>(</span><span>promise</span><span>, </span><span>x</span><span>, </span><span>resolve</span><span>, </span><span>reject</span><span>);</span></span></div></div><div><div><div>76</div></div><div><span><span>            </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>77</div></div><div><span>              </span><span>reject</span><span>(</span><span>this</span><span>.</span><span>#reason</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><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>80</div></div><div><span>            </span><span>reject</span><span><span>(</span><span>e</span><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>      </span></span><span>}</span></div></div><div><div><div>84</div></div><div><span>      </span><span>if</span><span> (</span><span>this</span><span>.</span><span>#status</span><span> </span><span>===</span><span><span> </span><span>PdPromise</span><span>.</span></span><span>PENDING</span><span>) {</span></div></div><div><div><div>85</div></div><div><span>        </span><span>this</span><span>.</span><span>#fulfilledCallbacks</span><span>.</span><span>push</span><span>(() </span><span>=&gt;</span></div></div><div><div><div>86</div></div><div><span>          </span><span>queueTask</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>87</div></div><div><span>            </span><span>try</span><span> {</span></div></div><div><div><div>88</div></div><div><span>              </span><span>resolveValue</span><span>(</span><span>this</span><span>.</span><span>#value</span><span>);</span></div></div><div><div><div>89</div></div><div><span><span>            </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>90</div></div><div><span>              </span><span>reject</span><span><span>(</span><span>e</span><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><span>        </span><span>this</span><span>.</span><span>#rejectedCallbacks</span><span>.</span><span>push</span><span>(() </span><span>=&gt;</span></div></div><div><div><div>95</div></div><div><span>          </span><span>queueTask</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>96</div></div><div><span>            </span><span>try</span><span> {</span></div></div><div><div><div>97</div></div><div><span>              </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>onrejectedBak</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span>) {</span></div></div><div><div><div>98</div></div><div><span>                </span><span>const</span><span> </span><span>x</span><span> </span><span>=</span><span> </span><span>onrejected</span><span>(</span><span>this</span><span>.</span><span>#reason</span><span>);</span></div></div><div><div><div>99</div></div><div><span>                </span><span>resolvePromise</span><span><span>(</span><span>promise</span><span>, </span><span>x</span><span>, </span><span>resolve</span><span>, </span><span>reject</span><span>);</span></span></div></div><div><div><div>100</div></div><div><span><span>              </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>101</div></div><div><span>                </span><span>reject</span><span>(</span><span>this</span><span>.</span><span>#reason</span><span>);</span></div></div><div><div><div>102</div></div><div><span><span>              </span></span><span>}</span></div></div><div><div><div>103</div></div><div><span><span>            </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>104</div></div><div><span>              </span><span>reject</span><span><span>(</span><span>e</span><span>);</span></span></div></div><div><div><div>105</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>106</div></div><div><span><span>          </span></span><span>})</span></div></div><div><div><div>107</div></div><div><span><span>        </span></span><span>);</span></div></div><div><div><div>108</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>109</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>110</div></div><div><span>    </span><span>return</span><span><span> </span><span>promise</span><span>;</span></span></div></div><div><div><div>111</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>112</div></div><div><span>}</span></div></div><div><div><div>113</div></div><div><span>function</span><span> </span><span>resolvePromise</span><span><span>(</span><span>promise</span><span>, </span><span>x</span><span>, </span><span>resolve</span><span>, </span><span>reject</span><span>) {</span></span></div></div><div><div><div>114</div></div><div><span>  </span><span>if</span><span><span> (</span><span>promise</span><span> </span></span><span>===</span><span><span> </span><span>x</span><span>) {</span></span></div></div><div><div><div>115</div></div><div><span>    </span><span>return</span><span> </span><span>reject</span><span>(</span><span>new</span><span> </span><span>TypeError</span><span>(</span><span>"The promise and the return value are the same"</span><span>));</span></div></div><div><div><div>116</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>117</div></div><div>
</div></div><div><div><div>118</div></div><div><span>  </span><span>if</span><span><span> (</span><span>x</span><span> </span></span><span>!==</span><span> </span><span>null</span><span> </span><span>&amp;&amp;</span><span> (</span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span> </span><span>||</span><span> </span><span>typeof</span><span><span> </span><span>x</span><span> </span></span><span>===</span><span> </span><span>"object"</span><span>)) {</span></div></div><div><div><div>119</div></div><div><span>    </span><span>let</span><span><span> </span><span>then</span><span>;</span></span></div></div><div><div><div>120</div></div><div><span>    </span><span>try</span><span> {</span></div></div><div><div><div>121</div></div><div><span><span>      </span></span><span>then</span><span> </span><span>=</span><span><span> </span><span>x</span><span>.</span></span><span>then</span><span>;</span></div></div><div><div><div>122</div></div><div><span><span>    </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>123</div></div><div><span>      </span><span>reject</span><span><span>(</span><span>e</span><span>);</span></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>// 如果then是一个函数,我们就理解这个x是一个promise</span></div></div><div><div><div>126</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>then</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span>) {</span></div></div><div><div><div>127</div></div><div><span>      </span><span>let</span><span><span> </span><span>called</span><span> </span></span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>128</div></div><div><span>      </span><span>try</span><span> {</span></div></div><div><div><div>129</div></div><div><span><span>        </span></span><span>then</span><span>.</span><span>call</span><span>(</span></div></div><div><div><div>130</div></div><div><span><span>          </span></span><span>x</span><span>,</span></div></div><div><div><div>131</div></div><div><span>          </span><span>function</span><span><span> (</span><span>y</span><span>) {</span></span></div></div><div><div><div>132</div></div><div><span>            </span><span>if</span><span><span> (</span><span>called</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>133</div></div><div><span><span>            </span></span><span>called</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>134</div></div><div><span>            </span><span>resolvePromise</span><span><span>(</span><span>promise</span><span>, </span><span>y</span><span>, </span><span>resolve</span><span>, </span><span>reject</span><span>);</span></span></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>function</span><span><span> (</span><span>r</span><span>) {</span></span></div></div><div><div><div>137</div></div><div><span>            </span><span>if</span><span><span> (</span><span>called</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>138</div></div><div><span><span>            </span></span><span>called</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>139</div></div><div><span>            </span><span>reject</span><span><span>(</span><span>r</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>);</span></div></div><div><div><div>142</div></div><div><span><span>      </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>143</div></div><div><span>        </span><span>if</span><span><span> (</span><span>called</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>144</div></div><div><span>        </span><span>reject</span><span><span>(</span><span>e</span><span>);</span></span></div></div><div><div><div>145</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>146</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>147</div></div><div><span>      </span><span>// 如果then不是一个函数,说明x只是有一个then属性普通对象</span></div></div><div><div><div>148</div></div><div><span>      </span><span>resolve</span><span><span>(</span><span>x</span><span>);</span></span></div></div><div><div><div>149</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>150</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>151</div></div><div><span>    </span><span>resolve</span><span><span>(</span><span>x</span><span>);</span></span></div></div><div><div><div>152</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>153</div></div><div><span>}</span></div></div><div><div><div>154</div></div><div><span><span>PdPromise</span><span>.</span></span><span>deferred</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>155</div></div><div><span>  </span><span>var</span><span><span> </span><span>result</span><span> </span></span><span>=</span><span> {};</span></div></div><div><div><div>156</div></div><div><span><span>  </span></span><span>result</span><span>.</span><span>promise</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>PdPromise</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>157</div></div><div><span><span>    </span></span><span>result</span><span>.</span><span>resolve</span><span> </span><span>=</span><span><span> </span><span>resolve</span><span>;</span></span></div></div><div><div><div>158</div></div><div><span><span>    </span></span><span>result</span><span>.</span><span>reject</span><span> </span><span>=</span><span><span> </span><span>reject</span><span>;</span></span></div></div><div><div><div>159</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>160</div></div><div><span>  </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>161</div></div><div><span>};</span></div></div><div><div><div>162</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span><span> </span><span>PdPromise</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>async/await<a href="#asyncawait"><span>#</span></a></h2><p>从<code>ES7</code>发布后,<code>async</code>以及<code>await</code>现在已经用得最多的一种异步解决方案,这两个关键字基本上成一对一(多)出现的</p><p>它让我们像书写同步代码一样书写异步代码,它让我们在书写异步代码时,没有了<code>嵌套</code>和<code>链式</code>的烦恼.</p><section><h4>　我们先来看下它的基础用法<a href="#我们先来看下它的基础用法"><span>#</span></a></h4><p>首先你要用 async 来标记一个函数是异步函数,然后在函数内部使用<code>await</code>来等待<code>Promise</code>的交付结果,让我们感觉就像是在书写同步代码一样,这样的一个好处是,我们在写异步代码的<code>嵌套</code>和<code>链式</code>都消失了.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>sleep</span><span> </span><span>=</span><span><span> (</span><span>time</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>PdPromise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> </span><span>resolve</span><span>(</span><span>`sleep </span><span>${</span><span>time</span><span>}</span><span>ms`</span><span><span>), </span><span>time</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>async</span><span> </span><span>function</span><span> </span><span>asyncFn</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> </span><span>res1</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>sleep</span><span>(</span><span>1000</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>res1</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>  </span><span>const</span><span> </span><span>res2</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>sleep</span><span>(</span><span>2000</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><span>res2</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>asyncFn</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>介绍完用法,我们是否也能自己写一版呢?答案是可以的.因为 async 和 await 本质上是 promise 和生成器迭代器的语法糖.那么借助生成器和迭代器,我们也可以实现自己的<code>async</code>以及<code>await</code>.</p><p>关于生成器和迭代器这一块就不详细展开了,感兴趣的可以去看一下红宝书相关的章节,讲的非常清楚.</p><p>既然生成器可以让我们暂停当前执行,等到下一次调用<code>next</code>才会再次恢复执行,那么我们可以结合 <code>promise</code> ,让 <code>promise</code> 在被解决或者被拒绝的时候的调用<code>next</code>,那么就可以模拟类似于 <code>async</code>和 <code>await</code> 的效果了.</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 实现模拟 async/await</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>myAsync</span><span><span>(</span><span>generatorFunc</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</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><span>const</span><span> </span><span>gen</span><span> </span><span>=</span><span><span> </span><span>generatorFunc</span><span>.</span></span><span>apply</span><span>(</span><span>this</span><span>, </span><span>arguments</span><span>);</span></div></div><div><div><div>6</div></div><div><span>    </span><span>// 返回一个promise,这个promise相当于一个启动promise,会在我们第一次调用next的时候被解决</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> </span><span>new</span><span> </span><span>PdPromise</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>8</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>9</div></div><div><span>      </span><span>// 这个value参数是上次调用next传递进来的参数</span></div></div><div><div><div>10</div></div><div><span>      </span><span>const</span><span> </span><span>next</span><span> </span><span>=</span><span><span> (</span><span>value</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>try</span><span> {</span></div></div><div><div><div>12</div></div><div><span>          </span><span>// 这个result中的value就是yield后边表达式返回的数据了,注意区分,容易搞混</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>result</span><span> </span><span>=</span><span><span> </span><span>gen</span><span>.</span></span><span>next</span><span><span>(</span><span>value</span><span>);</span></span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>} </span><span>catch</span><span><span> (</span><span>err</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>          </span><span>// 如果出现错误,直接拒绝这个Promise</span></div></div><div><div><div>16</div></div><div><span>          </span><span>reject</span><span><span>(</span><span>err</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></div></div><div><div><div>19</div></div><div><span>        </span><span>if</span><span><span> (</span><span>result</span><span>.</span></span><span>done</span><span>) {</span></div></div><div><div><div>20</div></div><div><span>          </span><span>// 如果已经完成,那么直接解决这个promise</span></div></div><div><div><div>21</div></div><div><span>          </span><span>resolve</span><span><span>(</span><span>result</span><span>.</span></span><span>value</span><span>);</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>} </span><span>else</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>// 这里我们会对value进行一次包装,保证一定会是一个Promise</span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>PdPromise</span><span>.</span><span>resolve</span><span><span>(</span><span>result</span><span>.</span></span><span>value</span><span>).</span><span>then</span><span>(</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>(</span><span>value</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span>              </span><span>// 在这个promise被解决的时候,调用next</span></div></div><div><div><div>28</div></div><div><span>              </span><span>next</span><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></span><span>(</span><span>reason</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>              </span><span>// 在这个promise被拒绝的时候,调用throw</span></div></div><div><div><div>32</div></div><div><span><span>              </span></span><span>gen</span><span>.</span><span>throw</span><span><span>(</span><span>reason</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><span>      </span></span><span>};</span></div></div><div><div><div>37</div></div><div>
</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>next</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></div></div><div><div><div>43</div></div><div><span>const</span><span> </span><span>sleep</span><span> </span><span>=</span><span><span> (</span><span>time</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>44</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>PdPromise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>45</div></div><div><span>    </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> </span><span>resolve</span><span>(</span><span>`sleep </span><span>${</span><span>time</span><span>}</span><span> ms`</span><span><span>), </span><span>time</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></div></div><div><div><div>48</div></div><div><span>function*</span><span> </span><span>generatorFunction</span><span>() {</span></div></div><div><div><div>49</div></div><div><span>  </span><span>// 这里的yield你就可以理解为await</span></div></div><div><div><div>50</div></div><div><span>  </span><span>const</span><span> </span><span>result1</span><span> </span><span>=</span><span> </span><span>yield</span><span> </span><span>sleep</span><span>(</span><span>1000</span><span>);</span></div></div><div><div><div>51</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"result1"</span><span><span>, </span><span>result1</span><span>);</span></span></div></div><div><div><div>52</div></div><div><span>  </span><span>const</span><span> </span><span>result2</span><span> </span><span>=</span><span> </span><span>yield</span><span> </span><span>sleep</span><span>(</span><span>2000</span><span>);</span></div></div><div><div><div>53</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"result2"</span><span><span>, </span><span>result2</span><span>);</span></span></div></div><div><div><div>54</div></div><div><span>  </span><span>const</span><span> </span><span>result3</span><span> </span><span>=</span><span> </span><span>yield</span><span> </span><span>"test no-promise"</span><span>;</span></div></div><div><div><div>55</div></div><div><span>  </span><span>return</span><span><span> [</span><span>result1</span><span>, </span><span>result2</span><span>, </span><span>result3</span><span>];</span></span></div></div><div><div><div>56</div></div><div><span>}</span></div></div><div><div><div>57</div></div><div><span>const</span><span> </span><span>asyncFunctionP</span><span> </span><span>=</span><span> </span><span>myAsync</span><span><span>(</span><span>generatorFunction</span><span>);</span></span></div></div><div><div><div>58</div></div><div><span><span>asyncFunctionP</span><span>.</span></span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>59</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>60</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>promise</code>的特性来帮我们来调用<code>next</code>.</p></section><section><h4>难道 async 和 await 只有好处吗?<a href="#难道-async-和-await-只有好处吗"><span>#</span></a></h4><p>难道说 async/await 就没有缺点吗?</p><p>几乎是的。但是他也有一些缺点:</p><ol>
<li>
<p>错误处理复杂化：
使用 async/await 时，我们需要使用 try/catch 来捕获错误，这可能会导致代码变得冗长，特别是在处理多个异步操作时。</p>
</li>
<li>
<p>并行执行困难：
默认情况下，await 会导致代码按顺序执行，这可能会影响性能。虽然可以使用 Promise.all() 来并行执行多个异步操作，但这需要额外的代码和思考。</p>
</li>
</ol><p>其实上边两点我是硬想出来的,所以可以几乎说 async/await 就是异步方案的最优解!</p></section></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p><code>发明promise的人真是个天才</code>😄</p><p>bye~</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/tuan-dui-dai-ma-feng-ge-tong-yi-fang-an/</id>
      <title type="text">团队代码风格统一方案</title>
      <published>2023-09-25T21:30:25.000Z</published>
      <updated>2023-09-25T21:30:25.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/tuan-dui-dai-ma-feng-ge-tong-yi-fang-an/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>今天碰到一个不大不小的问题<a href="#今天碰到一个不大不小的问题"><span>#</span></a></h2><p>前排提示：本片文章内容较为浅显，只适合作为一个抛砖引玉的作用，如果你希望得到一个较为详细的指南，那么本文可能不适合。</p><p>在平时工作中，如果大家的代码风格不统一，那么会出现的一个问题是，git记录会出现混乱的情况，导致合并代码变得较为困难，其难度呈指数型上升。所以，在日常工作中我们都会进行一些工程化配置来约束我们的代码风格，进而避免出现上述情况。</p></section>
<section><h2>话不多说,直接上方案<a href="#话不多说直接上方案"><span>#</span></a></h2><p>目前在前端主流方案中,通常是<code>prettier</code>和<code>eslint</code>,以及<code>editorconfig</code>三剑客来约束代码风格</p></section>
<section><h2>prettier<a href="#prettier"><span>#</span></a></h2><p>它是一个代码格式化工具,在一致的配置下,使用它格式化可以获得统一的代码风格.</p><p>使用也很简单,vscode安装它
<a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" target="_blank">https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode</a></p><p>然后在根目录创建<code>prettier.config.js</code>配置文件,将编辑器的默认格式化工具设置为prettier(这个总不用教吧 =.=)</p><p>简单的配置方案示例:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// https://www.prettier.cn/docs/install.html</span></div></div><div><div><div>2</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</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>printWidth</span><span>:</span><span> </span><span>150</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>tabWidth</span><span>:</span><span> </span><span>2</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>useTabs</span><span>:</span><span> </span><span>true</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>semi</span><span>:</span><span> </span><span>true</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>// 在对象文字中的括号之间打印空格</span></div></div><div><div><div>13</div></div><div><span> </span><span>bracketSpacing</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>14</div></div><div><span> </span><span>// jsx 标签的反尖括号需要换行</span></div></div><div><div><div>15</div></div><div><span> </span><span>jsxBracketSameLine</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>16</div></div><div><span> </span><span>// 在单独的箭头函数参数周围包括括号 always：(x) =&gt; x \ avoid：x =&gt; x</span></div></div><div><div><div>17</div></div><div><span> </span><span>arrowParens</span><span>:</span><span> </span><span>'always'</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>rangeStart</span><span>:</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>20</div></div><div><span> </span><span>rangeEnd</span><span>:</span><span> </span><span>Infinity</span><span>,</span></div></div><div><div><div>21</div></div><div><span> </span><span>// 指定要使用的解析器，不需要写文件开头的 @prettier</span></div></div><div><div><div>22</div></div><div><span> </span><span>requirePragma</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>23</div></div><div><span> </span><span>// 不需要自动在文件开头插入 @prettier</span></div></div><div><div><div>24</div></div><div><span> </span><span>insertPragma</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>25</div></div><div><span> </span><span>// 使用默认的折行标准 always\never\preserve</span></div></div><div><div><div>26</div></div><div><span> </span><span>proseWrap</span><span>:</span><span> </span><span>'preserve'</span><span>,</span></div></div><div><div><div>27</div></div><div><span> </span><span>// 指定HTML文件的全局空格敏感度 css\strict\ignore</span></div></div><div><div><div>28</div></div><div><span> </span><span>htmlWhitespaceSensitivity</span><span>:</span><span> </span><span>'css'</span><span>,</span></div></div><div><div><div>29</div></div><div><span> </span><span>singleQuote</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>30</div></div><div><span> </span><span>// 更改引用对象属性的时间 可选值"&lt;as-needed|consistent|preserve&gt;"</span></div></div><div><div><div>31</div></div><div><span> </span><span>quoteProps</span><span>:</span><span> </span><span>'as-needed'</span><span>,</span></div></div><div><div><div>32</div></div><div><span> </span><span>// 在JSX中使用单引号而不是双引号</span></div></div><div><div><div>33</div></div><div><span> </span><span>jsxSingleQuote</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>34</div></div><div><span> </span><span>// 多行时尽可能打印尾随逗号。（例如，单行数组永远不会出现逗号结尾。） 可选值"&lt;none|es5|all&gt;"，默认none</span></div></div><div><div><div>35</div></div><div><span> </span><span>trailingComma</span><span>:</span><span> </span><span>'none'</span><span>,</span></div></div><div><div><div>36</div></div><div><span> </span><span>// Vue文件脚本和样式标签缩进</span></div></div><div><div><div>37</div></div><div><span> </span><span>vueIndentScriptAndStyle</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>38</div></div><div><span> </span><span>// 换行符使用 lf 结尾是 可选值"&lt;auto|lf|crlf|cr&gt;"</span></div></div><div><div><div>39</div></div><div><span> </span><span>endOfLine</span><span>:</span><span> </span><span>'lf'</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><h2>eslint<a href="#eslint"><span>#</span></a></h2><p>通常我们不会自己去配置这个工具,因为规则实在是太多了,所以我们一般是直接使用社区的配置。
而在我们日常的开发中,一般脚手架都会默认帮我们创建这些内容,所以此处略过。</p><p>通常我们也会安装它的vscode拓展来更好的支持它
<a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" target="_blank">https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint</a></p></section>
<section><h2>editorconfig<a href="#editorconfig"><span>#</span></a></h2><p>有人喜欢用vscode，有人喜欢用webstorm，那怎么保持代码风格统一呢？那就要祭出这个大杀器</p><p>使用也很简单</p><p>安装拓展（webstorm插件商店也会有它，放心~）</p><p><a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig" target="_blank">https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig</a></p><p>在项目根目录创建<code>.editorconfig</code>文件，下边是一个简单的配置方案</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># Editor configuration, see http://editorconfig.org</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 表示是最顶层的 EditorConfig 配置文件</span></div></div><div><div><div>4</div></div><div><span>root = true</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>[*] # 表示所有文件适用</span></div></div><div><div><div>7</div></div><div><span>charset = utf-8 # 设置文件字符集为 utf-8</span></div></div><div><div><div>8</div></div><div><span>indent_style = space # 缩进风格（tab | space）</span></div></div><div><div><div>9</div></div><div><span>indent_size = 2 # 缩进大小</span></div></div><div><div><div>10</div></div><div><span>end_of_line = lf # 控制换行类型(lf | cr | crlf)</span></div></div><div><div><div>11</div></div><div><span>trim_trailing_whitespace = true # 去除行首的任意空白字符</span></div></div><div><div><div>12</div></div><div><span>insert_final_newline = true # 始终在文件末尾插入一个新行</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>[*.md] # 表示仅 md 文件适用以下规则</span></div></div><div><div><div>15</div></div><div><span>max_line_length = off</span></div></div><div><div><div>16</div></div><div><span>trim_trailing_whitespace = false</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>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/jie-shou-le-yi-ge-laya1-x-de-xiang-mu-wan-zheng-de-jie-shou-liu-cheng/</id>
      <title type="text">接手了一个laya1.x的项目,完整的接手流程</title>
      <published>2023-07-17T08:02:19.000Z</published>
      <updated>2023-07-17T08:02:19.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/jie-shou-le-yi-ge-laya1-x-de-xiang-mu-wan-zheng-de-jie-shou-liu-cheng/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>首先肯定要提升一下自己的开发体验<a href="#首先肯定要提升一下自己的开发体验"><span>#</span></a></h2><p>前面那个哥们使用的是<code>flashbuilder</code>这种上古神器来开发,这我哪能受得了啊,就一直想着能不能使用自己的熟悉的工具来开发呢?起初一开始我们对游戏开发一无所知,几乎就认定为只能使用<code>flashbuilder</code>来开发,而且那哥们一通配置下来,我们都觉得好繁琐,而且想把项目运行起来也很麻烦.</p><p>弄完那天刚好是周五,周末正好没事就研究了一下,发现那小哥配置的那些东西其实在项目里的配置文件里都有对应的位置去修改,不过他是使用了图形界面去配置,而我们的方式更极客(滑稽),直接修改配置文件内容.然后尝试了一下,发现是可以跑起来的,嘿嘿泰裤辣!这里我们其实已经可以使用vscode来开发了,但是我们还是想要更进一步,为了避免每次都去输入命令,我们在页面里加了一个package.json,主要是用来跑我们写的脚本,哈哈哈</p><p>但是目前还有两个严重的问题极其影响开发体验!</p><ul>
<li>修改完代码编译还是需要使用flashbuilder或者官方的IDE,但是我们就是偏偏要使用vscode来开发!</li>
<li>代码不支持智能提示,也不支持跳转定义,这个问题太严重了,这个项目代码量还是很大的,没有智能提示简直就是灾难!</li>
</ul><p>为了解决这两个问题,我们就开始了一系列的研究,最终的结果是,我们可以完全使用vscode来开发,并且支持智能提示和跳转定义!为了能避开其他IDE的介入,我们使用了官方的命令行工具<code>layaair-cmd</code>来进行编译,这样就可以不适用其他IDE了!但是代码不能跳转还是太痛苦了,最终无意之间发现在安装flashbuilder的时候,它提示了一个什么SDK,然后我去找了一下,发现是可以单独下载的,等我配置到环境变量里后,再次打开项目,发现智能提示和跳转定义都可以了,这下我们就可以完全使用vscode来开发了,哈哈哈~</p><p>vscode需要安装的插件:
AIRSDK32: <a href="https://www.adobe.com/devnet/air/air-sdk-download.html" target="_blank">https://www.adobe.com/devnet/air/air-sdk-download.html</a></p><p>ActionScript EXT:<a href="https://marketplace.visualstudio.com/items?itemName=bowlerhatllc.vscode-nextgenas" target="_blank">https://marketplace.visualstudio.com/items?itemName=bowlerhatllc.vscode-nextgenas</a></p></section>
<section><h2>既然要接手这个项目,当然要先去了解laya的一些基础知识了<a href="#既然要接手这个项目当然要先去了解laya的一些基础知识了"><span>#</span></a></h2><p>我把官方的文档大致的看了一下,然后去b站找了一些视频看看,发现其实内容量还好,感觉自己可以快速上手了.</p><p>至于ActionScript,我大致在项目里看了看,感觉没有学的必要,laya也只是借用了它的语法,相信在看项目的代码的时候自然而然就学会了~</p><p>好的,舒服的开发环境,必要的前置知识我们都具备了!接下来就是开始看项目了!</p></section>
<section><h2>从入口开始<a href="#从入口开始"><span>#</span></a></h2><p>公司不要这个项目了换cocos了,换个地儿开卷</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/yong-da-bai-hua-rang-ni-ming-bai-xu-ni-dom-de-diff-suan-fa/</id>
      <title type="text">用大白话让你明白虚拟DOM的DIFF算法</title>
      <published>2023-06-27T11:30:37.000Z</published>
      <updated>2023-06-27T11:30:37.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/yong-da-bai-hua-rang-ni-ming-bai-xu-ni-dom-de-diff-suan-fa/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>前文<a href="#前文"><span>#</span></a></h2><p>我们先来了解一些前置的必要知识.</p><section><h3>首先什么是虚拟dom?<a href="#首先什么是虚拟dom"><span>#</span></a></h3><p>虚拟DOM（Virtual DOM）其实就是一种存在于内存中的用来描述真实DOM的数据结构.其本质就是一堆对象,你可以通过渲染器将其渲染成真实DOM,它相比于真实的DOM数据来说更加轻量.下边用一个简单的例子来说明:</p><p>比如你有一个这样的真实DOM</p><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;1&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>div</span><span>&gt;2&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>4</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>那么,使用虚拟DOM来表示,他就是一个这样的对象</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>vdom</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>type</span><span>:</span><span> </span><span>"div"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>attr</span><span>:</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>id</span><span>:</span><span> </span><span>"app"</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>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>type</span><span>:</span><span> </span><span>"div"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>attr</span><span>:</span><span> {},</span></div></div><div><div><div>10</div></div><div><span>      </span><span>children</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><span>      </span><span>type</span><span>:</span><span> </span><span>"div"</span><span>,</span></div></div><div><div><div>14</div></div><div><span>      </span><span>attr</span><span>:</span><span> {},</span></div></div><div><div><div>15</div></div><div><span>      </span><span>children</span><span>:</span><span> </span><span>"2"</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><p>一般来说,这两者的转换的关系是单向的,即虚拟DOM转换为真实DOM;我们怎么实现这个转换呢?答案是通过渲染器,下边是一个简易的渲染器来帮助你理解:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>render</span><span><span>(</span><span>vdom</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>el</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span><span>(</span><span>vdom</span><span>.</span></span><span>type</span><span>);</span></div></div><div><div><div>3</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>vdom</span><span>.</span></span><span>attr</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>el</span><span>.</span><span>setAttribute</span><span><span>(</span><span>key</span><span>, </span><span>vdom</span><span>.</span></span><span>attr</span><span><span>[</span><span>key</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>if</span><span> (</span><span>typeof</span><span><span> </span><span>vdom</span><span>.</span></span><span>children</span><span> </span><span>===</span><span> </span><span>"string"</span><span>) {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>el</span><span>.</span><span>textContent</span><span> </span><span>=</span><span><span> </span><span>vdom</span><span>.</span></span><span>children</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>vdom</span><span>.</span><span>children</span><span>.</span><span>forEach</span><span><span>((</span><span>child</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>el</span><span>.</span><span>appendChild</span><span>(</span><span>render</span><span><span>(</span><span>child</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>}</span></div></div><div><div><div>13</div></div><div><span>  </span><span>return</span><span><span> </span><span>el</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>我们通过这个渲染器,可以很轻松的将虚拟DOM转换为真实DOM,然后将其添加到正确的挂载位置,页面可以正常显示了.这就是虚拟DOM,真实DOM,渲染器,三者的关系.虚拟DOM是更加抽象的存在,它的抽象程度脱离了渲染的平台,具体的转换后的输出内容要看你怎么实现的渲染器.</p></section><section><h3>我们为什么需要虚拟DOM?<a href="#我们为什么需要虚拟dom"><span>#</span></a></h3><p>好了,差不多说明了虚拟DOM之后,我们就要来说明一下为什么我们需要虚拟DOM了.它给我们带来了什么好处,解决了什么问题?
虚拟DOM主要解决了一个问题,避免了频繁的操作真实DOM,进而提高了性能.因为我们使用vue,react等框架时,我们更改和视图有关的数据后,直接反应到视图层面的其实就是虚拟DOM,操作内存中的虚拟DOM成本是比较低的,而操作真实DOM成本是比较高的.如果我们每次更改数据后,都从头到尾的重新生成一次真实DOM,那无疑成本是巨大的,所以我们可以在数据更改后生成新的虚拟DOM,然后与上一次生成的虚拟DOM来对比,进而找出实际需要更新的部分,然后复用没有更改的真实DOM,那么是不是性能就更高了呢?答案是几乎肯定的.</p><p>为什么说<code>几乎是肯定的</code>?其实如果你的页面实在是太简单,你会发现直接操作真实DOM性能更高,我们通常所说的虚拟DOM性能更高是指你的应用具一定体量,因为虚拟DOM在初始渲染需要做一些操作,以及在更新过程中需要保存一份副本来进行对比,内存占用会更高,所以你的应用如果实在太简单,而直接操作真实DOM可以直接跳过这些环节,根本享受不到虚拟DOM带来的好处哦.但是另一方面来说,从跨平台以及可维护性上来讲,没有理由不上虚拟DOM.</p></section></section>
<section><h2>DIFF算法<a href="#diff算法"><span>#</span></a></h2><p>前文我们已经提到过了,我们通过对比新旧虚拟DOM来找出实际需要更新的部分来提高性能.那么这个过程是如何实现的呢?这就是我们接下来要讨论的重点.我们过程中会根据vue3源码来解释这一过程.</p><p>我们先来总结一下可能出现的情况(n1表示旧节点,n2表示新节点):</p><ul>
<li>!n1&amp;&amp;n2 初始挂载</li>
<li>n1&amp;&amp;!n2 卸载</li>
<li>n1&amp;&amp;n2 都存在,就需要进行对比了
<ul>
<li>n1.type!==n2.type 如果是不同类型,需要先卸载旧节点,然后挂载新节点</li>
<li>n1.type===n2.type 如果是相同类型,进行patch,然后就是对比children了</li>
</ul>
</li>
</ul><p>接下来就是节点孩子的对比部分了, 本文重点要将的也是相同节点类型下children的对比,抛开框架本身的逻辑,那么children的类型只会出现这几种类型[text,‘array’],所以大体又会出现下面这几类情况</p><ul>
<li>!n1.children&amp;&amp;n2 挂载孩子节点</li>
<li>n1.children&amp;&amp;!n2.children 卸载孩子节点</li>
<li>n1.children&amp;&amp;n2.children 都存在,就需要进行对比了
<ul>
<li>n2.children.type===‘text’ 新节点孩子节点是文本,就需要先卸载旧的孩子节点,然后直接设置文本内容</li>
<li>n1.children.type===‘text’&amp;&amp;n1.children.type!==‘text’ 如果旧节点孩子节点是文本节点,新节点孩子节点不是文本节点,那么说明是一个数组,先将文本内容情况,然后将新节点的孩子节点进行挂载</li>
<li>n1.children.type!==‘text’&amp;&amp;n1.children.type!==‘text’ 都是数组类型,开启diff!</li>
</ul>
</li>
</ul><p>终于来到重点了,其实这部分才是主角,前边再多的情况无非是多开几个代码行进的分支,这部分才是最具含金量的~
那么children同样是数组的情况下,我们又来思考,会出现几种情况呢,大致会分为以下几类情况(我们只会讨论绑定了key值的孩子节点哈,没有绑定key值的节点可以使用简单粗暴的方式来解决):</p><p>尾部新增节点,那么这种我们就可以将左侧范围缩减</p><ul>
<li>(a b c d)</li>
<li>(a b c d) e f</li>
</ul><p>头部新增节点,那这种我们可以将右侧范围缩减</p><ul>
<li>(a b c d)</li>
<li>x y (a b c d)</li>
</ul><p>缩减之后,就是中间乱序的部分了,可能是新增,也可能是删除,还可能是交换了顺序,我们使用一个将所有情况都包含的例子</p><ul>
<li>(a b ) c d e f (g)</li>
<li>(a b ) d c x y f (g)
这个例子中我们交换了 c d 节点的位置,并添加了 x y两个节点,还删除掉了e节点</li>
</ul><p>接下来就是diff流程了,直接开始解读源代码哈(这一块的源码在patchKeyedChildren这个函数下,你可以自行搜索)</p><p>我们会定义个三个指针变量(问&lt;为什么不是四个指针呢&gt;?因为左侧就算定义两个指针,也总是从0开始,而右侧则是不固定的.)</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>l2</span><span> </span><span>=</span><span><span> </span><span>c2</span><span>.</span></span><span>length</span></div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>e1</span><span> </span></span><span>=</span><span><span> </span><span>c1</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>e2</span><span> </span></span><span>=</span><span><span> </span><span>l2</span><span> </span></span><span>-</span><span> </span><span>1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>先通过左侧和右侧对比来压缩我们需要diff的范围,这块直接贴源代码可能会更好理解哈,我会将源码做一定修改,只关注核心逻辑</p><p>左侧的对比,通过对比是否是同一个节点,来通过将i指针项右推动来压缩范围</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e1</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e2</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>n1</span><span> </span><span>=</span><span><span> </span><span>c1</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>n2</span><span> </span><span>=</span><span><span> </span><span>c2</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span> (</span><span>isSameVNodeType</span><span><span>(</span><span>n1</span><span>, </span><span>n2</span><span>)) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>patch</span><span>(</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>n1</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>n2</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>container</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>null</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>parentComponent</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><span>else</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>break</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>i</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>右侧是同理的</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e1</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e2</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>n1</span><span> </span><span>=</span><span><span> </span><span>c1</span><span>[</span><span>e1</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>n2</span><span> </span><span>=</span><span><span>c2</span><span>[</span><span>e2</span><span>]</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span> (</span><span>isSameVNodeType</span><span><span>(</span><span>n1</span><span>, </span><span>n2</span><span>)) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>patch</span><span>(</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>n1</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>n2</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>container</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>null</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>parentComponent</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><span>else</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>break</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>e1</span><span>--</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>e2</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>到这里双端对比就结束了,接下来我们需要根据三个指针的比较来判断现在是哪一种情况</p><ul>
<li>i &gt; e1 &amp;&amp; i &lt;= e2 i指针超过了e1指针,这种情况说明只是尾部添加了新节点,那我们只需要按顺序将节点进行挂载就可以了</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;</span><span><span> </span><span>e1</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e2</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>const</span><span> </span><span>nextPos</span><span> </span><span>=</span><span><span> </span><span>e2</span><span> </span></span><span>+</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>anchor</span><span> </span><span>=</span><span><span> </span><span>nextPos</span><span> </span></span><span>&lt;</span><span><span> </span><span>l2</span><span> </span></span><span>?</span><span><span> (</span><span>c2</span><span>[</span><span>nextPos</span><span>] </span><span>as</span><span> </span></span><span>VNode</span><span>).</span><span>el</span><span> </span><span>:</span><span><span> </span><span>parentAnchor</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e2</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>      </span><span>patch</span><span>(</span></div></div><div><div><div>7</div></div><div><span>        </span><span>null</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>c2</span><span>[</span><span>i</span><span>],</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>container</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>anchor</span><span>,</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>parentComponent</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>i</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></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><ul>
<li>i &gt; e2 &amp;&amp; i &lt;= e1 i大于e2却小于等于e1,说明有节点被删除掉了,那我们需要删除掉这些节点</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>else</span><span> </span><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;</span><span><span> </span><span>e2</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e1</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>unmount</span><span><span>(</span><span>c1</span><span>[</span><span>i</span><span>], </span><span>parentComponent</span><span>, </span><span>parentSuspense</span><span>, </span></span><span>true</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>i</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><ul>
<li>最后一种情况,i没有与右端任何指针相交,就是情况最复杂的时候了,这部分直接上源码吧,我会在源码中加入自己的见解说明,最后再大致说一下这之间的流程</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>else</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>s1</span><span> </span><span>=</span><span><span> </span><span>i</span><span> </span></span><span>// 旧列表开始索引</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>s2</span><span> </span><span>=</span><span><span> </span><span>i</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>// 构建一个新的索引表，用于记录新节点的key和索引的对应关系</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>keyToNewIndexMap</span><span>:</span><span> </span><span>Map</span><span><span>&lt;</span><span>string</span><span> </span></span><span>|</span><span><span> </span><span>number</span><span> </span></span><span>|</span><span><span> </span><span>symbol</span><span>, </span><span>number</span><span>&gt; </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>()</span></div></div><div><div><div>7</div></div><div><span>  </span><span>for</span><span><span> (</span><span>i</span><span> </span></span><span>=</span><span><span> </span><span>s2</span><span>; </span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e2</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>const</span><span> </span><span>nextChild</span><span> </span><span>=</span><span><span> </span><span>c2</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span><span> (</span><span>nextChild</span><span>.</span></span><span>key</span><span> </span><span>!=</span><span> </span><span>null</span><span>) {</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>keyToNewIndexMap</span><span>.</span><span>set</span><span><span>(</span><span>nextChild</span><span>.</span></span><span>key</span><span><span>, </span><span>i</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>}</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>j</span></span></div></div><div><div><div>15</div></div><div><span>  </span><span>// 用于记录已经被patch的节点的数量</span></div></div><div><div><div>16</div></div><div><span>  </span><span>let</span><span><span> </span><span>patched</span><span> </span></span><span>=</span><span> </span><span>0</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>const</span><span> </span><span>toBePatched</span><span> </span><span>=</span><span><span> </span><span>e2</span><span> </span></span><span>-</span><span><span> </span><span>s2</span><span> </span></span><span>+</span><span> </span><span>1</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>let</span><span><span> </span><span>maxNewIndexSoFar</span><span> </span></span><span>=</span><span> </span><span>0</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>let</span><span><span> </span><span>moved</span><span> </span></span><span>=</span><span> </span><span>false</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>  </span><span>// 构建一个新的索引表,用于记录可复用新旧节点的索引的对应关系</span></div></div><div><div><div>25</div></div><div><span>  </span><span>const</span><span> </span><span>newIndexToOldIndexMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Array</span><span><span>(</span><span>toBePatched</span><span>)</span></span></div></div><div><div><div>26</div></div><div><span>  </span><span>for</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>toBePatched</span><span>; </span><span>i</span></span><span>++</span><span><span>) </span><span>newIndexToOldIndexMap</span><span>[</span><span>i</span><span>] </span></span><span>=</span><span> </span><span>0</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>  </span><span>// 遍历旧节点</span></div></div><div><div><div>29</div></div><div><span>  </span><span>for</span><span><span> (</span><span>i</span><span> </span></span><span>=</span><span><span> </span><span>s1</span><span>; </span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e1</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>const</span><span> </span><span>prevChild</span><span> </span><span>=</span><span><span> </span><span>c1</span><span>[</span><span>i</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></div></div><div><div><div>33</div></div><div><span>    </span><span>if</span><span><span> (</span><span>patched</span><span> </span></span><span>&gt;=</span><span><span> </span><span>toBePatched</span><span>) {</span></span></div></div><div><div><div>34</div></div><div><span>      </span><span>// all new children have been patched so this can only be a removal</span></div></div><div><div><div>35</div></div><div><span>      </span><span>unmount</span><span><span>(</span><span>prevChild</span><span>, </span><span>parentComponent</span><span>, </span><span>parentSuspense</span><span>, </span></span><span>true</span><span>)</span></div></div><div><div><div>36</div></div><div><span>      </span><span>continue</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>let</span><span><span> </span><span>newIndex</span></span></div></div><div><div><div>40</div></div><div><span>    </span><span>// 如果旧节点存在key，那么就根据key去新节点中查找对应的索引</span></div></div><div><div><div>41</div></div><div><span>    </span><span>if</span><span><span> (</span><span>prevChild</span><span>.</span></span><span>key</span><span> </span><span>!=</span><span> </span><span>null</span><span>) {</span></div></div><div><div><div>42</div></div><div><span><span>      </span></span><span>newIndex</span><span> </span><span>=</span><span><span> </span><span>keyToNewIndexMap</span><span>.</span></span><span>get</span><span><span>(</span><span>prevChild</span><span>.</span></span><span>key</span><span>)</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>44</div></div><div><span>      </span><span>// 如果旧节点不存在key，那么就从新节点的最左侧开始查找与旧节点相同的节点</span></div></div><div><div><div>45</div></div><div><span>      </span><span>for</span><span><span> (</span><span>j</span><span> </span></span><span>=</span><span><span> </span><span>s2</span><span>; </span><span>j</span><span> </span></span><span>&lt;=</span><span><span> </span><span>e2</span><span>; </span><span>j</span></span><span>++</span><span>) {</span></div></div><div><div><div>46</div></div><div><span>        </span><span>if</span><span> (</span></div></div><div><div><div>47</div></div><div><span><span>          </span></span><span>newIndexToOldIndexMap</span><span>[</span><span>j</span><span> </span><span>-</span><span><span> </span><span>s2</span><span>] </span></span><span>===</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>48</div></div><div><span>          </span><span>isSameVNodeType</span><span><span>(</span><span>prevChild</span><span>, </span><span>c2</span><span>[</span><span>j</span><span>] </span><span>as</span><span> </span></span><span>VNode</span><span>)</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>newIndex</span><span> </span><span>=</span><span><span> </span><span>j</span></span></div></div><div><div><div>51</div></div><div><span>          </span><span>break</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><span>// 如果没有找到相同的节点，说明旧节点已经被移除了，直接卸载</span></div></div><div><div><div>56</div></div><div><span>    </span><span>if</span><span><span> (</span><span>newIndex</span><span> </span></span><span>===</span><span> </span><span>undefined</span><span>) {</span></div></div><div><div><div>57</div></div><div><span>      </span><span>unmount</span><span><span>(</span><span>prevChild</span><span>, </span><span>parentComponent</span><span>, </span><span>parentSuspense</span><span>, </span></span><span>true</span><span>)</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>59</div></div><div><span>      </span><span>// 如果找到了相同的节点，那么就将新节点的索引记录到索引表中,这里+1是避开索引为0的情况,因为我们初始值是0嘛</span></div></div><div><div><div>60</div></div><div><span><span>      </span></span><span>newIndexToOldIndexMap</span><span>[</span><span>newIndex</span><span> </span><span>-</span><span><span> </span><span>s2</span><span>] </span></span><span>=</span><span><span> </span><span>i</span><span> </span></span><span>+</span><span> </span><span>1</span></div></div><div><div><div>61</div></div><div><span>      </span><span>// 这里我们主要是判断节点是否需要移动,如果说新节点一直是升序排列的,那么说明节点是不需要移动的,否则就是移动过的,方便后续进行优化</span></div></div><div><div><div>62</div></div><div><span>      </span><span>if</span><span><span> (</span><span>newIndex</span><span> </span></span><span>&gt;=</span><span><span> </span><span>maxNewIndexSoFar</span><span>) {</span></span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>maxNewIndexSoFar</span><span> </span><span>=</span><span><span> </span><span>newIndex</span></span></div></div><div><div><div>64</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>moved</span><span> </span><span>=</span><span> </span><span>true</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>
</div></div><div><div><div>68</div></div><div><span>      </span><span>patch</span><span>(</span></div></div><div><div><div>69</div></div><div><span><span>        </span></span><span>prevChild</span><span>,</span></div></div><div><div><div>70</div></div><div><span><span>        </span></span><span>c2</span><span>[</span><span>newIndex</span><span>] </span><span>as</span><span> </span><span>VNode</span><span>,</span></div></div><div><div><div>71</div></div><div><span><span>        </span></span><span>container</span><span>,</span></div></div><div><div><div>72</div></div><div><span>        </span><span>null</span><span>,</span></div></div><div><div><div>73</div></div><div><span><span>        </span></span><span>parentComponent</span><span>,</span></div></div><div><div><div>74</div></div><div><span><span>        </span></span><span>parentSuspense</span><span>,</span></div></div><div><div><div>75</div></div><div><span><span>        </span></span><span>isSVG</span><span>,</span></div></div><div><div><div>76</div></div><div><span><span>        </span></span><span>slotScopeIds</span><span>,</span></div></div><div><div><div>77</div></div><div><span><span>        </span></span><span>optimized</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>
</div></div><div><div><div>80</div></div><div><span><span>      </span></span><span>patched</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>
</div></div><div><div><div>84</div></div><div><span>  </span><span>// 构建一个最长递增子序列的索引表,如果列表没有被移动过,那么说明根本不需要进行计算</span></div></div><div><div><div>85</div></div><div><span>  </span><span>// 构建这个列表的目的是尽可能多的找出不需要移动的节点,反之也就是说尽可能最少的移动节点来完成更新操作</span></div></div><div><div><div>86</div></div><div><span>  </span><span>const</span><span> </span><span>increasingNewIndexSequence</span><span> </span><span>=</span><span><span> </span><span>moved</span></span></div></div><div><div><div>87</div></div><div><span>    </span><span>?</span><span> </span><span>getSequence</span><span><span>(</span><span>newIndexToOldIndexMap</span><span>)</span></span></div></div><div><div><div>88</div></div><div><span>    </span><span>:</span><span> </span><span>EMPTY_ARR</span></div></div><div><div><div>89</div></div><div>
</div></div><div><div><div>90</div></div><div><span><span>  </span></span><span>j</span><span> </span><span>=</span><span><span> </span><span>increasingNewIndexSequence</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>  </span><span>// 接下来的思路是:遍历整个新节点,如果这个新节点在旧节点中不存在,那么我们会执行挂载操作,如果存在,我们就直接进行移动操作以达到复用的目的</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>  </span><span>// 这里倒序循环是因为锚点的问题</span></div></div><div><div><div>95</div></div><div><span>  </span><span>for</span><span><span> (</span><span>i</span><span> </span></span><span>=</span><span><span> </span><span>toBePatched</span><span> </span></span><span>-</span><span> </span><span>1</span><span><span>; </span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span><span>; </span><span>i</span></span><span>--</span><span>) {</span></div></div><div><div><div>96</div></div><div><span>    </span><span>const</span><span> </span><span>nextIndex</span><span> </span><span>=</span><span><span> </span><span>s2</span><span> </span></span><span>+</span><span><span> </span><span>i</span></span></div></div><div><div><div>97</div></div><div><span>    </span><span>const</span><span> </span><span>nextChild</span><span> </span><span>=</span><span><span> </span><span>c2</span><span>[</span><span>nextIndex</span><span>] </span><span>as</span><span> </span></span><span>VNode</span></div></div><div><div><div>98</div></div><div><span>    </span><span>// 对应的锚点一定要找准</span></div></div><div><div><div>99</div></div><div><span>    </span><span>const</span><span> </span><span>anchor</span><span> </span><span>=</span></div></div><div><div><div>100</div></div><div><span><span>      </span></span><span>nextIndex</span><span> </span><span>+</span><span> </span><span>1</span><span> </span><span>&lt;</span><span><span> </span><span>l2</span><span> </span></span><span>?</span><span><span> (</span><span>c2</span><span>[</span><span>nextIndex</span><span> </span></span><span>+</span><span> </span><span>1</span><span><span>] </span><span>as</span><span> </span></span><span>VNode</span><span>).</span><span>el</span><span> </span><span>:</span><span><span> </span><span>parentAnchor</span></span></div></div><div><div><div>101</div></div><div>
</div></div><div><div><div>102</div></div><div><span>    </span><span>// 如果索引表中的索引为0,说明这个节点在旧节点中不存在,那么我们就执行创建挂载操作</span></div></div><div><div><div>103</div></div><div><span>    </span><span>if</span><span><span> (</span><span>newIndexToOldIndexMap</span><span>[</span><span>i</span><span>] </span></span><span>===</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>104</div></div><div><span>      </span><span>patch</span><span>(</span></div></div><div><div><div>105</div></div><div><span>        </span><span>null</span><span>,</span></div></div><div><div><div>106</div></div><div><span><span>        </span></span><span>nextChild</span><span>,</span></div></div><div><div><div>107</div></div><div><span><span>        </span></span><span>container</span><span>,</span></div></div><div><div><div>108</div></div><div><span><span>        </span></span><span>anchor</span><span>,</span></div></div><div><div><div>109</div></div><div><span><span>        </span></span><span>parentComponent</span><span>,</span></div></div><div><div><div>110</div></div><div><span><span>        </span></span><span>parentSuspense</span><span>,</span></div></div><div><div><div>111</div></div><div><span><span>        </span></span><span>isSVG</span><span>,</span></div></div><div><div><div>112</div></div><div><span><span>        </span></span><span>slotScopeIds</span><span>,</span></div></div><div><div><div>113</div></div><div><span><span>        </span></span><span>optimized</span></div></div><div><div><div>114</div></div><div><span><span>      </span></span><span>)</span></div></div><div><div><div>115</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>moved</span><span>) {</span></span></div></div><div><div><div>116</div></div><div><span>      </span><span>// 然后是移动操作</span></div></div><div><div><div>117</div></div><div><span>      </span><span>// 判断一:如果j小于0,说明最长递增子序列已经遍历完了,那么剩下的都是需要移动的节点,可以不用判断这个,但这是个优化点</span></div></div><div><div><div>118</div></div><div><span>      </span><span>// 判断二:看一下当前索引是否能匹配到最长递增子序列中的索引,如果能匹配上,说明这个节点不需要移动,否则就需要移动</span></div></div><div><div><div>119</div></div><div><span>      </span><span>if</span><span><span> (</span><span>j</span><span> </span></span><span>&lt;</span><span> </span><span>0</span><span> </span><span>||</span><span><span> </span><span>i</span><span> </span></span><span>!==</span><span><span> </span><span>increasingNewIndexSequence</span><span>[</span><span>j</span><span>]) {</span></span></div></div><div><div><div>120</div></div><div><span>        </span><span>move</span><span><span>(</span><span>nextChild</span><span>, </span><span>container</span><span>, </span><span>anchor</span><span>, </span><span>MoveType</span><span>.</span></span><span>REORDER</span><span>)</span></div></div><div><div><div>121</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>122</div></div><div><span>        </span><span>// 如果能匹配上,说明我们就不需要进行移动操作了,直接跳过即可,由于我们是倒序,而且j初始值是最长递增子序列的长度-1,所以每次匹配上索引时j都会减1,直到小于0时,说明最长递增子序列已经遍历完了</span></div></div><div><div><div>123</div></div><div><span><span>        </span></span><span>j</span><span>--</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></span><span>}</span></div></div><div><div><div>126</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>127</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><ul>
<li>我们首先构建了一个新节点列表上的<code>key&gt;index</code>的map映射集,方便后续通过key来快速获取该节点在新列表中的索引</li>
<li>然后我们通过需要处理的新列表的长度来创建了一个数组,并全部填充为0,在将来能在旧列表中找到可复用节点是,索引上的值会被改变为在旧列表中的索引,后续如果索引上的值是0表示未找到复用的节点,即是一个新出现的节点,则需要创建进行挂载;依照这个逻辑我们对旧列表进行了遍历,通过绑定的key值来查找是否有对应key值的节点在新列表中,如果有就会进行记录,如果没有,说明这个节点需要被卸载(在这个遍历过程中,我们顺手将其卸载掉了)</li>
<li>然后我们通过新旧节点索引的数组构建出了一个最长递增子序列的数组,使用它能帮我们确定最长的不需要的移动的节点列表,关于最长递增子序列可自行去了解一下哦</li>
<li>数据都已经构建完毕了,开始干活吧,接下来我们就需要遍历新节点列表,我们进行了倒序遍历,这是因为锚点的缘故,根据前边构建数据时的逻辑,当新旧列表索引映射表中在当前索引为0时,表示需要创建挂载,然后就是在在旧列表中找到了对应节点,就看需不需要移动了,如果需要移动就移动,不需要移动就直接下一个.</li>
</ul><p>至此,整个diff过程就完成啦!</p></section>
<section><h2>wow,终于结束了~<a href="#wow终于结束了"><span>#</span></a></h2><p>可以很明显的感受到,最需要思考的一部分也即是我们讨论的最后一部分了,这个过程值得细细回味,太经典了~</p><p>结束,拜拜~</p><p>这部分内容在vue3源码位置</p><p><a href="https://github.com/vuejs/core/blob/main/packages/runtime-core/src/renderer.ts" target="_blank">diff源码位置</a></p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/shou-ba-shou-jiao-ni-lu-vue3-de-xiang-ying-xi-tong/</id>
      <title type="text">手把手教你撸vue3的响应系统</title>
      <published>2023-04-24T16:40:16.000Z</published>
      <updated>2023-04-24T16:40:16.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/shou-ba-shou-jiao-ni-lu-vue3-de-xiang-ying-xi-tong/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么会有这篇文章?<a href="#为什么会有这篇文章"><span>#</span></a></h2><p>最近在阅读<code>vue.js的设计与实现</code>这本书,在读到响应系统的时候,觉得有必要来记录下,自己也可以动手写一写书中的例子来加深理解.虽然以前有差不多完整的实现,但是都是跟着别人一步一步敲的,相当于没有自己的一部分思考,这是很可怕的一件事情,这次融合自己的思考实现一下vue3的响应系统的基础版本,加深自己对这一块的理解.你如果跟着文章敲,最终你会得到一个和我一样的响应系统~</p></section>
<section><h2>响应系统为我们带来了什么好处?<a href="#响应系统为我们带来了什么好处"><span>#</span></a></h2><p>首先,我想谈一下自己对响应式的认知.这块与书中内容无关,纯属自己瞎BB,可以直接跳过不看.记得第一次听到这个词语觉得很新鲜,就去了解了以下,发现其实在<code>数据</code>与<code>操作</code>之间实现一层逻辑来自动的处理某些事情,所以响应性编程是建立在代理一类的思想基础上的.如其名,响应性是指在数据发生改变的时候,其他地方会有一些响应性动作发生.通过建立的中间层来自动的帮我们完成一些事情,这样我们就可以只关心数据这一块的东西,逻辑的执行完全交给中间层来处理.</p><p>下边是一个小小的例子来解释这个说法,在这个例子中,当我们每次修改对象属性的时候,我们都需要发送一次请求</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>http</span><span> </span><span>=</span><span><span> (</span><span>val</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'http'</span><span><span>, </span><span>val</span><span>)</span></span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>KEY_PREFIX</span><span> </span><span>=</span><span> </span><span>'__'</span></div></div><div><div><div>6</div></div><div><span>// vue2时代</span></div></div><div><div><div>7</div></div><div><span><span>Object</span><span>.</span></span><span>defineProperty</span><span><span>(</span><span>obj</span><span>, </span></span><span>'a'</span><span>, {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>get</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>key</span><span> </span><span>=</span><span> </span><span>KEY_PREFIX</span><span> </span><span>+</span><span> </span><span>'a'</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span><span> </span><span>obj</span><span>[</span><span>key</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>set</span><span><span>(</span><span>value</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 如果这里不设置一个映射健,那么set操作就会导致是一个死循环</span></div></div><div><div><div>14</div></div><div><span>    </span><span>const</span><span> </span><span>key</span><span> </span><span>=</span><span> </span><span>KEY_PREFIX</span><span> </span><span>+</span><span> </span><span>'a'</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>http</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>obj</span><span>[</span><span>key</span><span>] </span><span>=</span><span><span> </span><span>value</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></div></div><div><div><div>20</div></div><div><span><span>obj</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>21</div></div><div><span><span>obj</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 使用Proxy的话,代码就更简单了</span></div></div><div><div><div>24</div></div><div><span>const</span><span> </span><span>obj3</span><span> </span><span>=</span><span> { </span><span>a</span><span>:</span><span> </span><span>1</span><span> }</span></div></div><div><div><div>25</div></div><div><span>const</span><span> </span><span>proxy</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Proxy</span><span><span>(</span><span>obj3</span><span>, {</span></span></div></div><div><div><div>26</div></div><div><span>  </span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>) {</span></span></div></div><div><div><div>27</div></div><div><span>    </span><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Reflect</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>)</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</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>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span>    </span><span>if</span><span><span> (</span><span>target</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>value</span><span>) {</span></span></div></div><div><div><div>32</div></div><div><span>      </span><span>return</span><span> </span><span>true</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Reflect</span><span>.</span></span><span>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>)</span></span></div></div><div><div><div>35</div></div><div><span>    </span><span>http</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>36</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</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><div><div><div>39</div></div><div><span><span>proxy</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>6</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>// 如果没有代理层的逻辑,我们完成本次需求的样子是这样的</span></div></div><div><div><div>42</div></div><div><span>const</span><span> </span><span>obj2</span><span> </span><span>=</span><span> { </span><span>a</span><span>:</span><span> </span><span>1</span><span> }</span></div></div><div><div><div>43</div></div><div><span><span>obj2</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>44</div></div><div><span>http</span><span><span>(</span><span>obj2</span><span>.</span></span><span>a</span><span>)</span></div></div><div><div><div>45</div></div><div><span><span>obj2</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>46</div></div><div><span>http</span><span><span>(</span><span>obj2</span><span>.</span></span><span>a</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>defineProperty</code>和<code>Proxy</code>的使用这里就不解释了,如果需要了解可以去mdn看一下:
<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty" target="_blank">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy" target="_blank">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy</a></p><p>可以明显感受到,使用代理的思想处理后,我们每次重新给对象的属性赋值,都会自动的触发我们定义的逻辑,在这个例子中,他会自动的发送一个请求,如果没有这一层的处理,那么发送请求的逻辑我们必须手动来实现,并且这些逻辑可能会散落到各个角落里,这就是响应性编程为我们带来的最直接也是最有用的好处!相信通过这个例子你已经切身体会到代理层的好处,然而我们这里只是简单到不能再简单的一个用例,离真正的响应系统还相差甚远,更别说是vue3所设计的响应系统了.下面我们先来简单实现一个响应性系统应该具备的基础要素.</p></section>
<section><h2>一个mini版本的响应系统实现<a href="#一个mini版本的响应系统实现"><span>#</span></a></h2><p>在实现之前,我们先思考一下我们可能会遇到的困难点</p><ul>
<li>如何将一个普通数据转成proxy这样的代理对象</li>
<li>如何在代理对象的get里收集依赖,依赖是指对象属性所关联的副作用函数,[属性]和[副作用函数]是一个多对多的关系</li>
<li>如何在代理对象的set里触发收集的依赖</li>
</ul><p>解决这三个问题也很简单</p><ul>
<li>第一个问题我们可以定义一个函数将数据传递进去然后返回它的代理对象</li>
<li>第二个问题相对来说比较需要思考,我们需要有一个数据结构来存储我们收集的依赖;我们需要留意两个地方&lt;不同对象之间依赖关系是相互独立的&gt;,同一个对象下不同key是所对应的依赖也是相互独立的,基于这两点我们就可以得到一个完整描述依赖关系的数据结构:</li>
</ul><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>new</span><span> </span><span>WeakMap</span><span>([</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>[</span><span>'obj1'</span><span>, </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>4</div></div><div><span>    </span><span>'key1'</span><span>, </span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>fn</span><span>, </span><span>fn</span><span>]),</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>'key2'</span><span>, </span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>fn</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>这里使用WeakMap是为了更好的垃圾回收,这种数据类型下的key会被垃圾回收机制忽略引用
接下来需要解决的问题是,我们又该如何收集副作用函数呢,我们需要有一个入口将你的副作用函数传递进来,然后将该副作用函数执行以触发代理对象的get钩子,但是我们在get钩子里如何拿到这个副作用函数呢,答案是在入口函数的地方将该这个副作用函数保存到某个地方,得益于js的单线程特性,一轮下来属性和副作用函数是能对应上的</p><ul>
<li>第三个问题就相对容易了,我们只需要找到对应依赖的副作用函数然后依次执行即可</li>
</ul><p>好了,路已经铺好,开始干活吧</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 当前的effect函数</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>activeEffect</span><span> </span></span><span>=</span><span> </span><span>null</span></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>targetMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>WeakMap</span><span>()</span></div></div><div><div><div>5</div></div><div><span>// 将普通数据转换为代理对象</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>toProxy</span><span> </span><span>=</span><span><span> (</span><span>val</span><span>) </span></span><span>=&gt;</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>return</span><span> </span><span>new</span><span> </span><span>Proxy</span><span><span>(</span><span>val</span><span>, {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>// get里主要的工作就是收集依赖以及将对应值返回</span></div></div><div><div><div>10</div></div><div><span>    </span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>receiver</span><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>track</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>)</span></span></div></div><div><div><div>13</div></div><div><span>      </span><span>// 对象的正常操作</span></div></div><div><div><div>14</div></div><div><span>      </span><span>return</span><span><span> </span><span>Reflect</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>receiver</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>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>      </span><span>// 这里是为了防止某些情况下的重复执行,下面有来自gpt的解释</span></div></div><div><div><div>18</div></div><div><span>      </span><span>if</span><span><span> (</span><span>target</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>value</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span>        </span><span>return</span><span> </span><span>true</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Reflect</span><span>.</span></span><span>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>)</span></span></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>trigger</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>)</span></span></div></div><div><div><div>24</div></div><div><span>      </span><span>return</span><span><span> </span><span>result</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></div></div><div><div><div>28</div></div><div><span>// 追踪函数</span></div></div><div><div><div>29</div></div><div><span>const</span><span> </span><span>track</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>30</div></div><div><span>  </span><span>let</span><span><span> </span><span>depMap</span></span></div></div><div><div><div>31</div></div><div><span>  </span><span>if</span><span><span> (</span><span>targetMap</span><span>.</span></span><span>has</span><span><span>(</span><span>target</span><span>)) {</span></span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>depMap</span><span> </span><span>=</span><span><span> </span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)</span></span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>targetMap</span><span>.</span><span>set</span><span><span>(</span><span>target</span><span>, (</span><span>depMap</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>// 将依赖收集到对应的key下</span></div></div><div><div><div>37</div></div><div><span>  </span><span>if</span><span><span> (</span><span>depMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>get</span><span><span>(</span><span>key</span><span>).</span></span><span>add</span><span><span>(</span><span>activeEffect</span><span>)</span></span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, </span></span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>activeEffect</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></div></div><div><div><div>43</div></div><div><span>// 触发函数</span></div></div><div><div><div>44</div></div><div><span>const</span><span> </span><span>trigger</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>45</div></div><div><span>  </span><span>// 逻辑很简单,通过路径拿到属于自己的副作用函数集合,然后执行</span></div></div><div><div><div>46</div></div><div><span><span>  </span></span><span>targetMap</span><span>.</span><span>get</span><span><span>(</span><span>target</span><span>)?.</span></span><span>get</span><span><span>(</span><span>key</span><span>)?.</span></span><span>forEach</span><span><span>(</span><span>effect</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>47</div></div><div><span>    </span><span>effect</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><span>}</span></div></div><div><div><div>50</div></div><div><span>// 接下来是入口函数</span></div></div><div><div><div>51</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>52</div></div><div><span><span>  </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>fn</span></span></div></div><div><div><div>53</div></div><div><span>  </span><span>fn</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>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({ </span><span>a</span><span>:</span><span> </span><span>1</span><span> })</span></div></div><div><div><div>57</div></div><div><span>effect</span><span>(()</span><span>=&gt;</span><span>{</span></div></div><div><div><div>58</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>state</span><span>.</span></span><span>a</span><span>)</span></div></div><div><div><div>59</div></div><div><span>})</span></div></div><div><div><div>60</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span>=</span><span>2</span></div></div><div><div><div>61</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span>=</span><span>3</span></div></div><div><div><div>62</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span>=</span><span>4</span></div></div><div><div><div>63</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span>=</span><span>5</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
关于Proxy的set钩子重复执行(来自gpt的解释)<p>Proxy 的 set 钩子一般会在属性被设置时执行，但是有些情况下可能会执行两次。以下是可能导致 set 钩子执行两次的情况：</p><p>当前属性的值已经是新值时，set 钩子也会被调用。这是因为在设置属性时，JavaScript 引擎会先检查当前值和新值是否相等，如果不相等才会执行 set 钩子。但是，如果当前值已经等于新值，由于引擎无法确定是否需要执行 set 钩子，因此会执行两次。</p><p>使用 Object.assign() 方法合并对象时，如果合并的对象中包含 Proxy 对象，那么 set 钩子也会被调用两次。这是因为 Object.assign() 方法在合并对象时会先拷贝属性值，然后再调用 set 钩子，导致 set 钩子被调用两次。</p><p>解决这个问题的方法是在 set 钩子中添加判断，如果当前值已经等于新值，则不执行后续操作。例如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Proxy</span><span>({}, {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span><span> (</span><span>target</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>value</span><span>) { </span></span><span>// 如果当前值已经等于新值，则不执行后续操作</span></div></div><div><div><div>4</div></div><div><span>      </span><span>return</span><span> </span><span>true</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></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>Reflect</span><span>.</span></span><span>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</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>上边我们已经实现了一个基本的响应系统,但其实还存在诸多问题:</p><ul>
<li>如果目标数据是基础类型该怎么办呢?,我们都知道vue中是专门实现了一个函数<code>ref</code>来处理这种情况,但是我们这里先简单一点处理,通过将基础类型包裹成引用类型来实现</li>
<li>effect函数里代码行进路径如果发生改变,那么就会出现遗留的副作用问题,因为如果在某些情况下代码的执行分支不会再访问某个属性,理想的情况下,当我们在修改这个属性的时候,就不应该再触发这个函数了!我们前边提到过属性和副作用函数是多对多的关系,一个属性可以对应多个副作用函数,同样的,一个副作用函数也可以被多个属性依赖,我们要做的也很简单,就是在每次副作用函数执行之前找到对应的属性,然后将该属性下的该副作用函数清理掉,然后执行副作用函数又会重新收集依赖,这样就可以实现依赖图谱的重新形成了,依赖只会包含最新的代码行进分支.那么新的问题出现了,我们应该怎么通过该副作用函数来找到对应的依赖合集呢,我们可以将传递进来的函数包裹一层,在其身上声明一个表示依赖合集的属性,用来存储副作用函数所对应的包含该副作用函数的依赖集合,这样我们在每次执行前都可以先将自身从集合中删除掉,然后去重新收集依赖,这样不同代码执行分支所遗留的问题也就得到了解决了.</li>
<li>如果你有定义多个响应数据,那么你会发现你在修改前边定义的数据时,最后定义的数据所关联的副作用函数会被错误执行,这是由于activeEffect没有被重新重置,在副作用函数执行时导致依赖被错误收集,我们有两种解决方案:
<ul>
<li>第一种&lt;在每次effect里完成收集后&gt;,都将activeEffect置空,然后在依赖收集处需要判断是否存在activeEffect,不存在就不进行收集,这样就可以解决了</li>
<li>第二种&lt;我们可以想办法在每次副作用函数执行时&gt;,将activeEffect函数重新指向自身所依赖的副作用函数,这样就不会再出现错误的收集</li>
</ul>
</li>
</ul><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>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>fn</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>activeEffect</span><span> </span><span>=</span><span> </span><span>null</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>// 追踪函数</span></div></div><div><div><div>8</div></div><div><span>const</span><span> </span><span>track</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</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>if</span><span> (</span><span>!</span><span><span>activeEffect</span><span>) </span></span><span>return</span></div></div><div><div><div>11</div></div><div><span>  </span><span>let</span><span><span> </span><span>depMap</span></span></div></div><div><div><div>12</div></div><div><span>  </span><span>if</span><span><span> (</span><span>targetMap</span><span>.</span></span><span>has</span><span><span>(</span><span>target</span><span>)) {</span></span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>depMap</span><span> </span><span>=</span><span><span> </span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)</span></span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>targetMap</span><span>.</span><span>set</span><span><span>(</span><span>target</span><span>, (</span><span>depMap</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>// 将依赖收集到对应的key下</span></div></div><div><div><div>18</div></div><div><span>  </span><span>if</span><span><span> (</span><span>depMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>get</span><span><span>(</span><span>key</span><span>).</span></span><span>add</span><span><span>(</span><span>activeEffect</span><span>)</span></span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, </span></span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>activeEffect</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>可以看到这种方式是简单且粗暴的,几乎没有任何可拓展性,所以我们肯定会优先选择第二种方案,端上来吧</p><p>第二种方案的改造</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>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>fn</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>effectFn</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>我们改造了effect入口函数,使用函数包裹一层,然后将这个函数作为副作用函数,这里的最重要的是将<code>activeEffect = effectFn</code>这段逻辑包含了进去,这使得以后每次执行副作用时activeEffect都会被赋值为自身依赖的函数,即使再次收集也不出现错误收集了,可以明显感受到,这种方式只需要改动一处地方,而且更具拓展性,毕竟是函数,那就有无限可能~</p><p>如果接下来是定义多个响应数据,也不会出现串台现象了~</p></section><section><h3>然后我们来解决第二个问题<a href="#然后我们来解决第二个问题"><span>#</span></a></h3><p>根据上边提到的,我们需要将<code>effect</code>函数改造一下,在改造后,他应该是下边这个样子</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</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>clean</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>fn</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></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>10</div></div><div><span>  </span><span>effectFn</span><span>()</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div><span>// 清理函数的逻辑也很简单,在将自身从依赖集合中删除后,将deps整个置空即可</span></div></div><div><div><div>13</div></div><div><span>const</span><span> </span><span>clean</span><span> </span><span>=</span><span><span> (</span><span>effectFn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span>  </span><span>const</span><span> </span><span>deps</span><span> </span><span>=</span><span><span> </span><span>effectFn</span><span>.</span></span><span>deps</span><span> </span><span>||</span><span> []</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>deps</span><span>.</span><span>forEach</span><span><span>(</span><span>dep</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>dep</span><span>.</span><span>delete</span><span><span>(</span><span>effectFn</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>effectFn</span><span>.</span><span>deps</span><span>.</span><span>length</span><span> </span><span>=</span><span> </span><span>0</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>可以看到,我们在在函数上声明了一个属性,它的作用是用来存储该副作用函数所对应的依赖集合,将来我们需要在每次执行副作用函数前从所有集合中删除自身,然后再次执行该副作用函数后,依赖又会被重新收集起来,等于依赖集合根据环境进行了一次更新.那么,我们应该在何时进行该函数所对应的依赖集合呢?那我们其实可以追踪的地方进行这个操作,那么我们来将追踪函数改造一下</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>const</span><span> </span><span>track</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>let</span><span><span> </span><span>depMap</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span><span> (</span><span>targetMap</span><span>.</span></span><span>has</span><span><span>(</span><span>target</span><span>)) {</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>depMap</span><span> </span><span>=</span><span><span> </span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</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></span><span>targetMap</span><span>.</span><span>set</span><span><span>(</span><span>target</span><span>, (</span><span>depMap</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>let</span><span><span> </span><span>dep</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>// 将依赖收集到对应的key下</span></div></div><div><div><div>11</div></div><div><span>  </span><span>if</span><span><span> (</span><span>depMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>dep</span><span> </span><span>=</span><span><span> </span><span>depMap</span><span>.</span></span><span>get</span><span><span>(</span><span>key</span><span>)</span></span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>dep</span><span>.</span><span>add</span><span><span>(</span><span>activeEffect</span><span>)</span></span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, (</span><span>dep</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>activeEffect</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>// 在这里将依赖集合给到副作用函数deps属性上</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>activeEffect</span><span>.</span><span>deps</span><span>.</span><span>push</span><span><span>(</span><span>dep</span><span>)</span></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>这样我们的改造也完成了,但是你会发现代码会无线循环,这是由于set在遍历时,如果当前遍历时将某个元素删除掉,而随后又添加了上去,那么循环会重新访问这个元素,我们这里就是这种情况,我们在触发函数里执行了effectFn函数,清理掉了自身,随后又被收集了进来,满足了无限访问这个元素的条件,所以造成了死循环,我们可以使用一个新的set集合来进行循环,不是同一个set集合,那么就不会造成这个问题了,来改造一下trigger吧</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>trigger</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)?.</span></span><span>get</span><span><span>(</span><span>key</span><span>) </span></span><span>||</span><span> []).</span><span>forEach</span><span><span>(</span><span>effect</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>effect</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><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>b</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>state2</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>6</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span><span>,</span></div></div><div><div><div>7</div></div><div><span>  </span><span>b</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>8</div></div><div><span>})</span></div></div><div><div><div>9</div></div><div><span>effect</span><span>(() </span><span>=&gt;</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><span>state</span><span>.</span></span><span>a</span><span>, </span><span>'state'</span><span>)</span></div></div><div><div><div>11</div></div><div><span>})</span></div></div><div><div><div>12</div></div><div><span>effect</span><span>(() </span><span>=&gt;</span><span> {</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>'reCall'</span><span>, </span><span>'state2'</span><span>)</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>state</span><span>.</span><span>b</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>state2</span><span>.</span></span><span>a</span><span>, </span><span>'state2'</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>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>state</span><span>.</span><span>a</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>()</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>state2</span><span>.</span><span>a</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>()</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>state</span><span>.</span><span>b</span><span> </span><span>=</span><span> </span><span>!</span><span><span>state</span><span>.</span></span><span>b</span></div></div><div><div><div>21</div></div><div><span>}, </span><span>1000</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>重点当然是直接改造toProxy函数了啦</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>toProxy</span><span> </span><span>=</span><span><span> (</span><span>val</span><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>targetVal</span><span> </span></span><span>=</span><span><span> </span><span>val</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>isReference</span><span> </span><span>=</span><span> </span><span>isObject</span><span><span>(</span><span>val</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>isReference</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>targetVal</span><span> </span><span>=</span><span> { </span><span>value</span><span>:</span><span><span> </span><span>val</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></div></div><div><div><div>8</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Proxy</span><span><span>(</span><span>targetVal</span><span>, {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>// get里主要的工作就是收集依赖以及将对应值返回</span></div></div><div><div><div>10</div></div><div><span>    </span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>isReference</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>key</span><span> </span></span><span>!==</span><span> </span><span>'value'</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'请使用.value获取值'</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></div></div><div><div><div>16</div></div><div><span>      </span><span>track</span><span><span>(</span><span>target</span><span>, </span><span>key</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>return</span><span><span> </span><span>Reflect</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>receiver</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>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>isReference</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>key</span><span> </span></span><span>!==</span><span> </span><span>'value'</span><span>) {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'请使用.value设置值'</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>// 这里是为了防止某些情况下的重复执行,下面有来自gpt的解释</span></div></div><div><div><div>26</div></div><div><span>      </span><span>if</span><span><span> (</span><span>target</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>value</span><span>) {</span></span></div></div><div><div><div>27</div></div><div><span>        </span><span>return</span><span> </span><span>true</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Reflect</span><span>.</span></span><span>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><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>trigger</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>)</span></span></div></div><div><div><div>32</div></div><div><span>      </span><span>return</span><span><span> </span><span>result</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></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>我们在返回代理对象前进行了一些判断,然后强制用户使用我们特定的key去访问原始值,这样就实现了~</p></section><section><h3>然后是一些附加功能<a href="#然后是一些附加功能"><span>#</span></a></h3><p>为了让我们的响应系统更加强大,我们需要拓展一些功能</p><section><h4>可嵌套的effect<a href="#可嵌套的effect"><span>#</span></a></h4><p>很明显,我们现在所设计的响应系统是无法支持嵌套的,但是这种需求是很常见的,比如在vue中,组件一层套一层,而他们的render函数不就是一个副作用函数吗?那么我们现在来将其改造成可支持嵌套的</p><p>如果你熟悉栈这个数据结构,那么你肯定能很快想象到我们需要使用这个数据结构,我们的函数调用栈不也是类似的结构吗;在前边,我们只有一个<code>activeEffect</code>来支撑我们的逻辑,但是如果需要支持嵌套,我们肯定还需要一个栈来存储因为effect嵌套所压进来的副作用函数.然后我们再执行副作用函数时将函数压栈,执行完毕就出栈,那么副作用函数就能一一对应,不会再出现错乱的问题了</p><p>首先是栈结构</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 支撑嵌套逻辑的栈,如果用数组来模拟,那么你应该只去调用pop,以及push这两个api</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>effectFnStack</span><span> </span><span>=</span><span> []</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后就是effect函数了!</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</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>clean</span><span><span>(</span><span>effectFn</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></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>fn</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>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// 将栈顶的函数赋值给activeEffect</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFnStack</span><span>[</span><span>effectFnStack</span><span>.</span></span><span>length</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></div></div><div><div><div>14</div></div><div><span>  </span><span>// 在该函数身上声明一个属性,用来储存该函数所对应的依赖set</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>16</div></div><div><span>  </span><span>effectFn</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>然后我们来测试一下</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 测试嵌套effect函数</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>3</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>state2</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>6</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>7</div></div><div><span>})</span></div></div><div><div><div>8</div></div><div><span>effect</span><span>(() </span><span>=&gt;</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>state</span><span>.</span></span><span>a</span><span>)</span></div></div><div><div><div>10</div></div><div><span>  </span><span>effect</span><span>(() </span><span>=&gt;</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>state2</span><span>.</span></span><span>a</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><span><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>444</span></div></div><div><div><div>15</div></div><div><span><span>state2</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>555</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>可以发现,我们的目的达到了,当我们修改最外层的state的属性时,联动内部的响应数据的副作用函数也执行了.而我们只修改内部的响应数据时,外层的响应数据所对应的副作用函数不会被执行.但是你会发现,内部的函数会被执行两次,这是由于我们在effect内每次收集的都是一个全新的函数,即使你的函数内容完全一样,还是会新建一个函数,这样就导致Set集合的特性也无法帮我们规避重复函数了,我们应该怎么解决这个问题呢?其实也简单,我们可以将fn挂在到我们新建的函数上,然后每次去检查对应的依赖函数中是否有同样的fn存在,如果存在,那我们就不再进行收集,所以我们需要改动两个地方</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</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>clean</span><span><span>(</span><span>effectFn</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></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>fn</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>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// 将栈顶的函数赋值给activeEffect</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFnStack</span><span>.</span></span><span>at</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></div></div><div><div><div>14</div></div><div><span>  </span><span>// 在该函数身上声明一个属性,用来储存该函数所对应的依赖set</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>16</div></div><div><span>  </span><span>// 将外部函数挂载到effectFn上</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>fn</span><span> </span><span>=</span><span><span> </span><span>fn</span></span></div></div><div><div><div>18</div></div><div><span>  </span><span>effectFn</span><span>()</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>我们将fn挂载到了effectFn上,方便后续收集的时候,进行排除</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>const</span><span> </span><span>track</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>let</span><span><span> </span><span>depMap</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span><span> (</span><span>targetMap</span><span>.</span></span><span>has</span><span><span>(</span><span>target</span><span>)) {</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>depMap</span><span> </span><span>=</span><span><span> </span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</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></span><span>targetMap</span><span>.</span><span>set</span><span><span>(</span><span>target</span><span>, (</span><span>depMap</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>let</span><span><span> </span><span>dep</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>// 将依赖收集到对应的key下</span></div></div><div><div><div>11</div></div><div><span>  </span><span>if</span><span><span> (</span><span>depMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>dep</span><span> </span><span>=</span><span><span> </span><span>depMap</span><span>.</span></span><span>get</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>// + 这里收集的时候,我们就需要来判断是否是否存在同样的fn了</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>([</span><span>...</span><span><span>dep</span><span>]).</span></span><span>every</span><span><span>((</span><span>f</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>f</span><span>.</span></span><span>fn</span><span> </span><span>!==</span><span><span> </span><span>activeEffect</span><span>.</span></span><span>fn</span><span>) </span><span>&amp;&amp;</span><span><span> </span><span>dep</span><span>.</span></span><span>add</span><span><span>(</span><span>activeEffect</span><span>)</span></span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, (</span><span>dep</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>activeEffect</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>// 在这里将依赖集合给到副作用函数deps属性上</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>activeEffect</span><span>.</span><span>deps</span><span>.</span><span>push</span><span><span>(</span><span>dep</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>这里我们在收集函数的时候进行了一个前置比较,如果依赖中已经有包含该外部fn的effectFn,那么我们就不再进行收集</p><p>好的,接下来进行测试</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 测试嵌套effect函数</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>3</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>state2</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>6</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>7</div></div><div><span>})</span></div></div><div><div><div>8</div></div><div><span>const</span><span> </span><span>fn1</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>state</span><span>.</span></span><span>a</span><span>)</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>fn2</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>state2</span><span>.</span></span><span>a</span><span>)</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div><span>effect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span>  </span><span>fn1</span><span>()</span></div></div><div><div><div>16</div></div><div><span>  </span><span>effect</span><span><span>(</span><span>fn2</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><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>444</span></div></div><div><div><div>19</div></div><div><span><span>state2</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>555</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>你会发现,我们的目的达到了!你可能还发现了,噫?我们前边的测试代码不是这个样子的呀,这是因为每次全新匿名函数是无法进行比对相等的,所以我进行了一定的改造,其实这样也很合理,你想,一个vue组件的render函数初始化后也不会改变其引用地址~如果你非就要使用前边那样的方式,我暂时还没有想到更好的办法,这本书中也没有提及这个问题,如果你有更好的方法可以联系我.</p></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>obj</span><span>.</span></span><span>num</span><span>=</span><span><span>obj</span><span>.</span></span><span>num</span><span>+</span><span>1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>看似很正常的一个操作,但是他会带来无限循环,我们来理一下它的执行流程:</p><ul>
<li>首先=右边我们读取了obj.num,那么就会导致这个函数被当成依赖进行收集</li>
<li>然后在读取之后,我们进行了+1,最后将其赋值给了obj.num,那么因为此时依赖已经被进行了收集,所以会直接触发这个函数</li>
<li>再次运行这个函数,流程再次重复,所以这个操作会导致无限循环执行,不意外了吧</li>
</ul><p>我们只需要改造一个地方即可解决这个问题,我们在副作用函数执行的地方来做下边的改动:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>trigger</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)?.</span></span><span>get</span><span><span>(</span><span>key</span><span>) </span></span><span>||</span><span> []).</span><span>forEach</span><span><span>(</span><span>effect</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>effect</span><span> </span><span>!==</span><span><span> </span><span>activeEffect</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>effect</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></section><section><h4>调度执行<a href="#调度执行"><span>#</span></a></h4><p>有时候,我们希望我们能让用户自己来决定副作用执行的时机,将副作用函数的执行权交到用户手里,那么我们应该怎么做呢?我们可以在effect函数的地方增加一些参数,这个参数是一些可以让用户自己配置的选项,该参数是一个对象,如果后续需要,也更方便我们来拓展其它功能.</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>options</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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>clean</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>fn</span><span>()</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFnStack</span><span>.</span></span><span>at</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></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>fn</span><span> </span><span>=</span><span><span> </span><span>fn</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></span><span>effectFn</span><span>.</span><span>options</span><span> </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>14</div></div><div><span>  </span><span>effectFn</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><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>trigger</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)?.</span></span><span>get</span><span><span>(</span><span>key</span><span>) </span></span><span>||</span><span> []).</span><span>forEach</span><span><span>(</span><span>effect</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>effect</span><span>.</span></span><span>options</span><span>.</span><span>scheduler</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>effect</span><span>.</span><span>options</span><span>.</span><span>scheduler</span><span><span>(</span><span>effect</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></span><span>effect</span><span> </span><span>!==</span><span><span> </span><span>activeEffect</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>effect</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></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>const</span><span> </span><span>scheduler</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</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>'scheduler'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>fn</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>'scheduler end'</span><span>);</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>7</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>8</div></div><div><span>})</span></div></div><div><div><div>9</div></div><div><span>effect</span><span>(() </span><span>=&gt;</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>'effect'</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>state</span><span>.</span></span><span>a</span><span>);</span></div></div><div><div><div>12</div></div><div><span>}, {</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>scheduler</span></div></div><div><div><div>14</div></div><div><span>})</span></div></div><div><div><div>15</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span> </span><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><p>你会发现功能如我们所期望的实现了~</p><p>但是这个调度器的使用场景在哪里呢?那我们来一些有意义的实战解释吧,相信大家都知道我们在vue组件内更改多次更改数据后,vue并不会每次都进行试图更新,而是在你同步代码所有数据更改完毕后一次性更新视图,依此来避免了视图务无必要的频繁更新,那么vue是怎么做到的呢?答案是利用异步特性和我们自定义调度器,vue将视图更新的操作放到了异步队列,他会根据你的环境来选择最合适的异步任务类型,而组件的render就是充分利用了响应系统的可调度性来实现的,我们下边来演示一下这个东西</p><p>我们肯定首先要来实现一个符合我们需求的调度器</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 任务缓存队列,vue官方使用的是一个数组,我们使用set是为了利用其不重复的特性</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>queue</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</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>isFlushing</span><span> </span></span><span>=</span><span> </span><span>false</span></div></div><div><div><div>5</div></div><div><span>// 创建一个立即 resolve 的 Promise 实例,用来在微任务中刷新任务队列,vue实际上会根据环境来选择使用哪种方式</span></div></div><div><div><div>6</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>7</div></div><div><span>// 调度器的主要函数，用来将一个任务添加到缓冲队列中，并开始刷新队列</span></div></div><div><div><div>8</div></div><div><span>function</span><span> </span><span>queueJob</span><span><span>(</span><span>job</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>  </span><span>// 将 job 添加到任务队列 queue 中</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>queue</span><span>.</span><span>add</span><span><span>(</span><span>job</span><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>if</span><span> (</span><span>!</span><span><span>isFlushing</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 将该标志设置为 true 以避免重复刷新</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>isFlushing</span><span> </span><span>=</span><span> </span><span>true</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></span><span>p</span><span>.</span><span>then</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span>      </span><span>try</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></span><span>queue</span><span>.</span><span>forEach</span><span><span>(</span><span>job</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>20</div></div><div><span>          </span><span>job</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><span>finally</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>isFlushing</span><span> </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>queue</span><span>.</span><span>clear</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>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>上边就实现了一个简易的调度器,它的作用就和vue组件的更新机制一样,将函数放置在一个微任务里来执行,这样即使我们在同步代码内频繁的往调度器里传递副作用函数,那么由于这里调度器Set+异步的特性那么其实副作用函数也只会触发一次!</p><p>测似一下吧~</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>render</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>'render'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>new</span><span> </span><span>Array</span><span>(</span><span>50</span><span>).</span><span>fill</span><span>().</span><span>forEach</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>queueJob</span><span><span>(</span><span>render</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>我们一次性往调度器内传入了50个render函数,但是函数只执行了一次,完美达成效果.有了这个调度器,那我们模拟vue的更新机制岂不是易如反掌?好,完整模拟一次</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>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>3</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>})</span></div></div><div><div><div>5</div></div><div><span>// 模拟vue3组件的更新函数</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>update</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>state</span><span>.</span></span><span>a</span><span>,</span><span>'update'</span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div><span>effect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>  </span><span>update</span><span>()</span></div></div><div><div><div>11</div></div><div><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>scheduler</span><span>:</span><span><span> </span><span>queueJob</span></span></div></div><div><div><div>14</div></div><div><span>})</span></div></div><div><div><div>15</div></div><div><span>// 我们这里连续更改了3次数据</span></div></div><div><div><div>16</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>17</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>18</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>4</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>你会发现update函数一共执行了两次,第一次是effect自动执行一次,随后我们连续更改了三次响应数据,但是update函数并没有执行三次,而是执行了一次!我们的目的达到了~</p></section><section><h4>懒执行<a href="#懒执行"><span>#</span></a></h4><p>你可以发现,我们现在的effect函数是没有懒执行的能力的,我们传入的函数被立即执行了,你可以先停下来想一想,有没有办法延迟到一定时候再来执行呢?</p><p>好了,直接公布答案,我们可以通过一个属性来判断是否立即执行,如果是需要懒执行,我们就不立即执行,而是把这个函数返回,让外部自己去确定执行时机(注意,这个时候依赖是没有进行收集的哟~)</p><p>好,我们接下来进行改造effect函数:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>options</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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>clean</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>fn</span><span>()</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFnStack</span><span>.</span></span><span>at</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></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>fn</span><span> </span><span>=</span><span><span> </span><span>fn</span></span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>options</span><span> </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>13</div></div><div><span>  </span><span>// 直接看这里,改造的是在这里哟~</span></div></div><div><div><div>14</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>options</span><span>.</span></span><span>lazy</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>effectFn</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>return</span><span><span> </span><span>effectFn</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>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>21</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>22</div></div><div><span>})</span></div></div><div><div><div>23</div></div><div><span>const</span><span> </span><span>fn</span><span> </span><span>=</span><span> </span><span>effect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>state</span><span>.</span></span><span>a</span><span>, </span><span>'state.a'</span><span>);</span></div></div><div><div><div>25</div></div><div><span>}, {</span></div></div><div><div><div>26</div></div><div><span>  </span><span>lazy</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>27</div></div><div><span>})</span></div></div><div><div><div>28</div></div><div><span>debugger</span></div></div><div><div><div>29</div></div><div><span>fn</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><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>options</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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>clean</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>// 1</span></div></div><div><div><div>7</div></div><div><span>    </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>fn</span><span>()</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFnStack</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>)</span></div></div><div><div><div>10</div></div><div><span>    </span><span>// 2</span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</span><span><span> </span><span>res</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>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>fn</span><span> </span><span>=</span><span><span> </span><span>fn</span></span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>options</span><span> </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>16</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>options</span><span>.</span></span><span>lazy</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>effectFn</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><span>effectFn</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>res</code>保存了真正的副作用函数执行后的结果,在最后将其返回~那么接下来,我们就可以实现一个懒执行的<code>computed</code>的api了!</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 计算属性api</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>computed</span><span><span>(</span><span>getter</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span><span>(</span><span>getter</span><span>, { </span></span><span>lazy</span><span>:</span><span> </span><span>true</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>get</span><span> </span><span>value</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>return</span><span> </span><span>effectFn</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></code></pre><div><div></div><div></div></div></figure></div><p>是不是比你想象的简单?计算属性返回一个对象,如果没有去读取value这个属性,那么getter函数就永远不执行,避免性能浪费!都进行到这里了,我们继续实现一下计算属性的缓存功能吧,很简单,上代码!</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>computed</span><span><span>(</span><span>getter</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>value</span><span>, </span><span>dirty</span><span> </span></span><span>=</span><span> </span><span>true</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>scheduler</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>dirty</span><span> </span><span>=</span><span> </span><span>true</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>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span><span>(</span><span>getter</span><span>, { </span></span><span>lazy</span><span>:</span><span> </span><span>true</span><span><span>, </span><span>scheduler</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>get</span><span> </span><span>value</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>      </span><span>if</span><span><span> (</span><span>dirty</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>value</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>dirty</span><span> </span><span>=</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><span>      </span><span>return</span><span><span> </span><span>value</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></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>解释下我们干了什么&lt;我们首先新声明了两个变量&gt;,value用来缓存值,dirty表示是否是脏值,如果是就需要重新执行副作用函数了,在进行了第一次计算后,dirty变为了false,这导致了我们永久不再进行计算新值的问题,为了解决这个问题,我们使用了调度器来将dirty重新设置为了true(注意我们这里并不需要接受或者执行调度器传递进来的副作用函数,因为我们这里的计算属性在读取value的时候才需要去执行).哈哈,就这样,我们就实现了一个懒执行并且还具有缓存功能的计算属性api了!</p><p>别着急,还有最后一个小问题,如果你尝试在effect中使用计算属性,你会发现即使数据更改了,effect函数也没有执行,这是因为啥啊?我们来分析一下,感觉这个问题有点类似我们前边提到的effect嵌套问题呢,我们不是已经解决了吗,怎么还是会出现这么奇怪的问题?这是因为我们计算属性返回的对象并没有使用响应函数去包裹,那么意味着这个对象本身是不会走我们依赖收集和触发那一套逻辑的,那咋办呢?那我们就自己来收集和触发,这样也可以形成依赖关系.开工~</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>computed</span><span><span>(</span><span>getter</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>value</span><span>, </span><span>dirty</span><span> </span></span><span>=</span><span> </span><span>true</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>scheduler</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>dirty</span><span> </span><span>=</span><span> </span><span>true</span></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>trigger</span><span><span>(</span><span>obj</span><span>, </span></span><span>'value'</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>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span><span>(</span><span>getter</span><span>, { </span></span><span>lazy</span><span>:</span><span> </span><span>true</span><span><span>, </span><span>scheduler</span><span> })</span></span></div></div><div><div><div>9</div></div><div><span>  </span><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>get</span><span> </span><span>value</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>      </span><span>if</span><span><span> (</span><span>dirty</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>value</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>dirty</span><span> </span><span>=</span><span> </span><span>false</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>// 1</span></div></div><div><div><div>16</div></div><div><span>      </span><span>track</span><span><span>(</span><span>obj</span><span>, </span></span><span>'value'</span><span>)</span></div></div><div><div><div>17</div></div><div><span>      </span><span>return</span><span><span> </span><span>value</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>return</span><span><span> </span><span>obj</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>wow简直完美</p></section></section><section><h3>watch函数<a href="#watch函数"><span>#</span></a></h3><p>既然我们都实现了计算属性api,那咋可能不来实现一下他的兄弟api<code>watch</code>函数呢?你其实可能已经发现,我们所实现的effect函数不就像一个watch吗?其实并不是,我们在使用watch这个api时,是可以明确指定依赖项的,而我们的effect函数,只要内部相数据发生改变就会触发,但是watch只有在依赖项发生变化时才会去执行,我们的effct函数更像时<code>watchEffect</code>这个api</p><p>我们先上一个简单的版本</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>effect</span><span>(()</span><span>=&gt;</span><span><span>obj</span><span>.</span></span><span>x</span><span>, {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>scheduler</span><span>:</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>cb</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></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上面的代码就实现了一个简单版本的watch了,我们利用了调度系统来执行我们的回调函数,当我们去更改obj.x的值时就会执行回调.但是这个api也太简陋了吧,不仅没有新值旧值的参数,而且也没有立即执行以及深度监听的方法,别着急,一步一步慢慢来~</p><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>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>effect</span><span>(</span><span>traverse</span><span><span>(</span><span>obj</span><span>), {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>scheduler</span><span>:</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>cb</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></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>// 这个函数主要用来访问一个对象下的所有后代属性</span></div></div><div><div><div>9</div></div><div><span>function</span><span> </span><span>traverse</span><span><span>(</span><span>value</span><span>, </span><span>seen</span><span> </span></span><span>=</span><span> </span><span>new</span><span><span> </span><span>Set</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span>isObject</span><span><span>(</span><span>value</span><span>) </span></span><span>||</span><span><span> </span><span>seen</span><span>.</span></span><span>has</span><span><span>(</span><span>value</span><span>)) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</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></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>seen</span><span>.</span><span>add</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>15</div></div><div><span>  </span><span>if</span><span><span> (</span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>value</span><span>)) {</span></span></div></div><div><div><div>16</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>value</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>      </span><span>traverse</span><span><span>(</span><span>value</span><span>[</span><span>i</span><span>], </span><span>seen</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><span>else</span><span> {</span></div></div><div><div><div>20</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>value</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span>      </span><span>traverse</span><span><span>(</span><span>value</span><span>[</span><span>key</span><span>], </span><span>seen</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><p>可以看到,我们多出来了一个traverse函数,我们使用它来递归的访问属性,以达到触发依赖收集的目的</p><p>另外,我还有一个地方进行了调整,在effect函数中,我发现如果副作用函数堆栈中已经没有函数了,那就不需要更改当前副作用函数的指向了所以我做了以下调整,注意有注释的行:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>options</span></span><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>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>clean</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>fn</span><span>()</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>8</div></div><div><span>    </span><span>// 如果栈中没有函数了,就不需要再改变activeEffect了</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>length</span><span> </span><span>&amp;&amp;</span><span><span> (</span><span>activeEffect</span><span> </span></span><span>=</span><span><span> </span><span>effectFnStack</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>))</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span><span> </span><span>res</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>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>fn</span><span> </span><span>=</span><span><span> </span><span>fn</span></span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>options</span><span> </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>15</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>options</span><span>.</span></span><span>lazy</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>effectFn</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>return</span><span><span> </span><span>effectFn</span></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></section><section><h4>让watch支持自定义的getter函数<a href="#让watch支持自定义的getter函数"><span>#</span></a></h4><p>我们都知道在watch中可以选择传入一个响应式对象或者自定义一个getter,我们已经实现了前者,接下来我们来实现后者.如果你传入的是一个函数那么这个函数就可以当成是一个getter,只有getter函数内使用的数据发现变化,才会触发回调,这就是大致思路,接下来是实现:</p><p>我们将watch改造成了下边的样子,主要是更改了effect函数传入的地方</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>isGetter</span><span> </span><span>=</span><span> </span><span>isFunction</span><span><span>(</span><span>obj</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>effect</span><span>(</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>isGetter</span><span> </span><span>?</span><span><span> </span><span>obj</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>traverse</span><span><span>(</span><span>obj</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>scheduler</span><span>:</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>cb</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></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>新值与旧值<a href="#新值与旧值"><span>#</span></a></h4><p>你可能发现我们还缺少一个重要功能,那就是在回调中的新值与旧值的参数我们还没有实现,要实现这个功能我们需要充分使用使用前边我们实现的懒执行的功能,其实与计算属性那块儿的逻辑是差不多的,我们需要更改的地方有两个,一个是watch函数(让其支持懒执行,并获得新值与旧值的交替),一个是深度访问函数traverse(主要是需要一个返回值),接下里实现一下吧:</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>function</span><span> </span><span>traverse</span><span><span>(</span><span>value</span><span>, </span><span>seen</span><span> </span></span><span>=</span><span> </span><span>new</span><span><span> </span><span>Set</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span>isObject</span><span><span>(</span><span>value</span><span>) </span></span><span>||</span><span><span> </span><span>seen</span><span>.</span></span><span>has</span><span><span>(</span><span>value</span><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>return</span><span><span> </span><span>value</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>seen</span><span>.</span><span>add</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>  </span><span>if</span><span><span> (</span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>value</span><span>)) {</span></span></div></div><div><div><div>9</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>value</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>      </span><span>traverse</span><span><span>(</span><span>value</span><span>[</span><span>i</span><span>], </span><span>seen</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>} </span><span>else</span><span> {</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>value</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>      </span><span>traverse</span><span><span>(</span><span>value</span><span>[</span><span>key</span><span>], </span><span>seen</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></div></div><div><div><div>18</div></div><div><span>  </span><span>return</span><span><span> </span><span>value</span></span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div><span>// 充分利用effect函数的懒执行</span></div></div><div><div><div>21</div></div><div><span>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>  </span><span>let</span><span><span> </span><span>newVal</span><span>, </span><span>oldVal</span></span></div></div><div><div><div>23</div></div><div><span>  </span><span>const</span><span> </span><span>isGetter</span><span> </span><span>=</span><span> </span><span>isFunction</span><span><span>(</span><span>obj</span><span>)</span></span></div></div><div><div><div>24</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span>(</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>isGetter</span><span> </span><span>?</span><span><span> </span><span>obj</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>traverse</span><span><span>(</span><span>obj</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>lazy</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>28</div></div><div><span>      </span><span>scheduler</span><span>:</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>newVal</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>30</div></div><div><span>        </span><span>cb</span><span><span>(</span><span>newVal</span><span>, </span><span>oldVal</span><span>)</span></span></div></div><div><div><div>31</div></div><div><span>        </span><span>// 更新旧值</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>oldVal</span><span> </span><span>=</span><span><span> </span><span>newVal</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></div></div><div><div><div>36</div></div><div><span><span>  </span></span><span>oldVal</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>37</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><p>我们在使用watch的时候还可以指定两个经常用的参数,flush和immediate,flush用来指定回调执行的时机,而immediate是一个boolean值,用来表示是否回调会立即执行,而本次执行不会关心数据是否发生了变化</p><p>flush的可选值:</p><ul>
<li>pre 默认行为,你的回调会在vue进行更新前被执行</li>
<li>sync 数据发生变化后立即被执行,可能伴随性能问题</li>
<li>post 在vue进行更新完成后执行你的回调,这个时候在你的回调里能获取到更新完成后的dom结构</li>
</ul><p>immediate的可选值: true|false,默认值false</p><p>我们要完成这个功能,需要继续改造我们的watch函数,为它增加第三个参数来完成</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>, </span><span>options</span></span><span>=</span><span>{}) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>newVal</span><span>, </span><span>oldVal</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> { </span><span>flush</span><span> </span><span>=</span><span> </span><span>'pre'</span><span>, </span><span>immediate</span><span> } </span><span>=</span><span><span> </span><span>options</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>const</span><span> </span><span>job</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>newVal</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>7</div></div><div><span>    </span><span>cb</span><span><span>(</span><span>newVal</span><span>, </span><span>oldVal</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>oldVal</span><span> </span><span>=</span><span><span> </span><span>newVal</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></div></div><div><div><div>11</div></div><div><span>  </span><span>const</span><span> </span><span>jobFlushMap</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>// vue的更新周期钩子有关系,我们就不实现了,意思一下得了</span></div></div><div><div><div>13</div></div><div><span>    </span><span>pre</span><span>:</span><span> () </span><span>=&gt;</span><span><span> </span><span>job</span><span>,</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>// 这里直接使用我们前边的queueJob来模拟</span></div></div><div><div><div>15</div></div><div><span>    </span><span>post</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>queueJob</span><span><span>(</span><span>job</span><span>),</span></span></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>sync</span><span>:</span><span><span> </span><span>job</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>const</span><span> </span><span>isGetter</span><span> </span><span>=</span><span> </span><span>isFunction</span><span><span>(</span><span>obj</span><span>)</span></span></div></div><div><div><div>20</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span>(</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>isGetter</span><span> </span><span>?</span><span><span> </span><span>obj</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>traverse</span><span><span>(</span><span>obj</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>lazy</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>24</div></div><div><span>      </span><span>// 根据选项去挑选对应的调度函数,如果你传入了其他意外值那么就使用默认行为</span></div></div><div><div><div>25</div></div><div><span>      </span><span>scheduler</span><span>:</span><span><span> </span><span>jobFlushMap</span><span>[</span><span>flush</span><span>] </span></span><span>||</span><span><span> </span><span>jobFlushMap</span><span>[</span></span><span>'pre'</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></div></div><div><div><div>28</div></div><div><span>  </span><span>if</span><span><span> (</span><span>immediate</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>job</span><span>()</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>// 获取到初始值</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>oldVal</span><span> </span><span>=</span><span> </span><span>effectFn</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></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>就这样我们就完成了,更改的地方都伴随有注释,相信你可以看懂;这里会有一个问题是,我们使用立即执行功能,会发现
oldVal是一个undifined,初看觉得是不应该的,但是其实这个逻辑是对的,因为第一次执行根本不存在有旧值的说法</p><p>因为flsuh的pre选项会涉及到vue的运行时相关的逻辑,我们暂时是无法模拟的,感兴趣可以去看一下源码,搜一下<code>updateComponent</code>这个函数,你会看到在组件更新进行patch之前会调用<code>updateComponentPreRender</code>,里边就是组件更新前执行的逻辑,其中就包含刷新执行我们使用pre选项的watch回调函数</p><p>书中还提到了一个有意思的东西&lt;过期的副作用函数&gt;.你可以思考这么一个场景,你有一个响应式数据,然后你先后触发了回调函数,但是这个回调里比较特殊的情况是有一个异步行为,你在使用的过程中发现有时候得不到期望的运行结果,最后发现原来是异步行为的原因,由于你后触发的回调的异步执行有时候会慢于前一次,所以导致回调里的行为被前一次覆盖掉了.我们还是使用我们最熟悉的代码来解释一下这个行为:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</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>res</span><span> </span></span><span>=</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>let</span><span><span> </span><span>i</span></span><span>=</span><span>0</span></div></div><div><div><div>7</div></div><div><span>watch</span><span><span>(</span><span>state</span><span>, () </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>i</span><span>++</span></div></div><div><div><div>9</div></div><div><span>  </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>res</span><span> </span><span>=</span><span><span> </span><span>i</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></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>i</span><span>===</span><span>2</span><span>?</span><span>2000</span><span>:</span><span>3000</span><span>)</span></div></div><div><div><div>14</div></div><div><span>})</span></div></div><div><div><div>15</div></div><div><span><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>16</div></div><div><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>state</span><span>.</span><span>a</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>18</div></div><div><span>}, </span><span>100</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在上边的示例中,我们其实希望res最后的结果是2,但是其结果最终却是1,这就是因为第二次异步行为耗时慢了一些的原因.</p><p>我们可以给watch的回调函数第三个参数,他是一个函数,专门用来注册让副作用函数失效的方法,然后让其他地方的回调函数去执行这些方法,让前边的回调变为失效的回调,那我们的目的就达到了,好,我们来改造一下</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>, </span><span>options</span><span> </span></span><span>=</span><span> {}) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>newVal</span><span>, </span><span>oldVal</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> { </span><span>flush</span><span> </span><span>=</span><span> </span><span>'pre'</span><span>, </span><span>immediate</span><span> </span><span>=</span><span> </span><span>false</span><span> } </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>cleanups</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></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>onInvalidate</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>cleanups</span><span>.</span><span>add</span><span><span>(</span><span>fn</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>const</span><span> </span><span>job</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>newVal</span><span> </span><span>=</span><span> </span><span>effectFn</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></span><span>cleanups</span><span>.</span><span>forEach</span><span><span>(</span><span>cleanup</span><span> </span></span><span>=&gt;</span><span> </span><span>cleanup</span><span>())</span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 把onInvalidate函数传递给回调函数</span></div></div><div><div><div>14</div></div><div><span>    </span><span>cb</span><span><span>(</span><span>newVal</span><span>, </span><span>oldVal</span><span>, </span><span>onInvalidate</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>oldVal</span><span> </span><span>=</span><span><span> </span><span>newVal</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>const</span><span> </span><span>isGetter</span><span> </span><span>=</span><span> </span><span>isFunction</span><span><span>(</span><span>obj</span><span>)</span></span></div></div><div><div><div>19</div></div><div><span>  </span><span>const</span><span> </span><span>jobFlushMap</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>pre</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>job</span><span>(),</span></div></div><div><div><div>21</div></div><div><span>    </span><span>post</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>queueJob</span><span><span>(</span><span>job</span><span>),</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>sync</span><span>:</span><span><span> </span><span>job</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>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span>(</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>isGetter</span><span> </span><span>?</span><span><span> </span><span>obj</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>traverse</span><span><span>(</span><span>obj</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>lazy</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>28</div></div><div><span>      </span><span>scheduler</span><span>:</span><span><span> </span><span>jobFlushMap</span><span>[</span><span>flush</span><span>] </span></span><span>||</span><span><span> </span><span>job</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>if</span><span><span> (</span><span>immediate</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span>    </span><span>job</span><span>()</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>oldVal</span><span> </span><span>=</span><span> </span><span>effectFn</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></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>const</span><span> </span><span>state</span><span> </span><span>=</span><span> </span><span>toProxy</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>a</span><span>:</span><span> </span><span>1</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>res</span><span> </span></span><span>=</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>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span></div></div><div><div><div>7</div></div><div><span>watch</span><span><span>(</span><span>state</span><span>, (</span><span>n</span><span>, </span><span>o</span><span>, </span><span>onInvalidate</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>i</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><span>expire</span><span> </span></span><span>=</span><span> </span><span>false</span></div></div><div><div><div>11</div></div><div><span>  </span><span>onInvalidate</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>expire</span><span> </span><span>=</span><span> </span><span>true</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>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>expire</span><span>) </span></span><span>return</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>res</span><span> </span><span>=</span><span><span> </span><span>i</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></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>i</span><span> </span><span>===</span><span> </span><span>2</span><span> </span><span>?</span><span> </span><span>1000</span><span> </span><span>:</span><span> </span><span>2000</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><span>state</span><span>.</span></span><span>a</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>23</div></div><div><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>state</span><span>.</span><span>a</span><span> </span><span>=</span><span> </span><span>3</span></div></div><div><div><div>25</div></div><div><span>}, </span><span>100</span><span>)</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>setTimeout</span><span>(() </span><span>=&gt;</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>res</span><span>);</span></span><span>//2</span></div></div><div><div><div>29</div></div><div><span>}, </span><span>3000</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></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>const</span><span> </span><span>isObject</span><span> </span><span>=</span><span><span> (</span><span>val</span><span>) </span></span><span>=&gt;</span><span> </span><span>typeof</span><span><span> </span><span>val</span><span> </span></span><span>===</span><span> </span><span>'object'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>val</span><span> </span></span><span>!==</span><span> </span><span>null</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>isFunction</span><span> </span><span>=</span><span><span> (</span><span>val</span><span>) </span></span><span>=&gt;</span><span> </span><span>typeof</span><span><span> </span><span>val</span><span> </span></span><span>===</span><span> </span><span>'function'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 任务缓存队列,vue官方使用的是一个数组,我们使用set是为了利用其不重复的特性</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>queue</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>()</span></div></div><div><div><div>6</div></div><div><span>// 一个标志，代表是否正在刷新任务队列</span></div></div><div><div><div>7</div></div><div><span>let</span><span><span> </span><span>isFlushing</span><span> </span></span><span>=</span><span> </span><span>false</span></div></div><div><div><div>8</div></div><div><span>// 创建一个立即 resolve 的 Promise 实例,用来在微任务中刷新任务队列,vue实际上会根据环境来选择使用哪种方式</span></div></div><div><div><div>9</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>10</div></div><div><span>// 调度器的主要函数，用来将一个任务添加到缓冲队列中，并开始刷新队列</span></div></div><div><div><div>11</div></div><div><span>function</span><span> </span><span>queueJob</span><span><span>(</span><span>job</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>  </span><span>// 将 job 添加到任务队列 queue 中</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>queue</span><span>.</span><span>add</span><span><span>(</span><span>job</span><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>if</span><span> (</span><span>!</span><span><span>isFlushing</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>// 将该标志设置为 true 以避免重复刷新</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>isFlushing</span><span> </span><span>=</span><span> </span><span>true</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></span><span>p</span><span>.</span><span>then</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>20</div></div><div><span>      </span><span>try</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>queue</span><span>.</span><span>forEach</span><span><span>(</span><span>job</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>23</div></div><div><span>          </span><span>job</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><span>finally</span><span> {</span></div></div><div><div><div>26</div></div><div><span>        </span><span>// 重置状态</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>isFlushing</span><span> </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>queue</span><span>.</span><span>clear</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><span>  </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div><div><div><div>33</div></div><div><span>// 当前的effect函数</span></div></div><div><div><div>34</div></div><div><span>let</span><span><span> </span><span>activeEffect</span><span> </span></span><span>=</span><span> </span><span>null</span></div></div><div><div><div>35</div></div><div><span>// 支撑嵌套逻辑的栈,用数组来模拟,那么你应该只去调用pop,以及push</span></div></div><div><div><div>36</div></div><div><span>const</span><span> </span><span>effectFnStack</span><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>const</span><span> </span><span>targetMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>WeakMap</span><span>()</span></div></div><div><div><div>39</div></div><div><span>// 将普通数据转换为代理对象</span></div></div><div><div><div>40</div></div><div><span>const</span><span> </span><span>toProxy</span><span> </span><span>=</span><span><span> (</span><span>val</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>41</div></div><div><span>  </span><span>let</span><span><span> </span><span>targetVal</span><span> </span></span><span>=</span><span><span> </span><span>val</span></span></div></div><div><div><div>42</div></div><div><span>  </span><span>const</span><span> </span><span>isReference</span><span> </span><span>=</span><span> </span><span>typeof</span><span><span> </span><span>val</span><span> </span></span><span>===</span><span> </span><span>'object'</span></div></div><div><div><div>43</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>isReference</span><span>) {</span></span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>targetVal</span><span> </span><span>=</span><span> { </span><span>value</span><span>:</span><span><span> </span><span>val</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></div></div><div><div><div>47</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Proxy</span><span><span>(</span><span>targetVal</span><span>, {</span></span></div></div><div><div><div>48</div></div><div><span>    </span><span>// get里主要的工作就是收集依赖以及将对应值返回</span></div></div><div><div><div>49</div></div><div><span>    </span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>50</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>isReference</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>key</span><span> </span></span><span>!==</span><span> </span><span>'value'</span><span>) {</span></div></div><div><div><div>51</div></div><div><span>        </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'请使用.value获取值'</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></div></div><div><div><div>54</div></div><div><span>      </span><span>track</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>)</span></span></div></div><div><div><div>55</div></div><div><span>      </span><span>// 对象的正常操作</span></div></div><div><div><div>56</div></div><div><span>      </span><span>return</span><span><span> </span><span>Reflect</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>receiver</span><span>)</span></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>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>) {</span></span></div></div><div><div><div>59</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>isReference</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>key</span><span> </span></span><span>!==</span><span> </span><span>'value'</span><span>) {</span></div></div><div><div><div>60</div></div><div><span>        </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'请使用.value设置值'</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>
</div></div><div><div><div>63</div></div><div><span>      </span><span>// 这里是为了防止某些情况下的重复执行,下面有来自gpt的解释</span></div></div><div><div><div>64</div></div><div><span>      </span><span>if</span><span><span> (</span><span>target</span><span>[</span><span>key</span><span>] </span></span><span>===</span><span><span> </span><span>value</span><span>) {</span></span></div></div><div><div><div>65</div></div><div><span>        </span><span>return</span><span> </span><span>true</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Reflect</span><span>.</span></span><span>set</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>, </span><span>value</span><span>, </span><span>receiver</span><span>)</span></span></div></div><div><div><div>68</div></div><div><span>      </span><span>// 触发副作用函数</span></div></div><div><div><div>69</div></div><div><span>      </span><span>trigger</span><span><span>(</span><span>target</span><span>, </span><span>key</span><span>)</span></span></div></div><div><div><div>70</div></div><div><span>      </span><span>return</span><span><span> </span><span>result</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>  </span></span><span>})</span></div></div><div><div><div>73</div></div><div><span>}</span></div></div><div><div><div>74</div></div><div><span>// 追踪函数</span></div></div><div><div><div>75</div></div><div><span>const</span><span> </span><span>track</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>76</div></div><div><span>  </span><span>let</span><span><span> </span><span>depMap</span></span></div></div><div><div><div>77</div></div><div><span>  </span><span>if</span><span><span> (</span><span>targetMap</span><span>.</span></span><span>has</span><span><span>(</span><span>target</span><span>)) {</span></span></div></div><div><div><div>78</div></div><div><span><span>    </span></span><span>depMap</span><span> </span><span>=</span><span><span> </span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)</span></span></div></div><div><div><div>79</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>80</div></div><div><span><span>    </span></span><span>targetMap</span><span>.</span><span>set</span><span><span>(</span><span>target</span><span>, (</span><span>depMap</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>let</span><span><span> </span><span>dep</span></span></div></div><div><div><div>83</div></div><div><span>  </span><span>// 将依赖收集到对应的key下</span></div></div><div><div><div>84</div></div><div><span>  </span><span>if</span><span><span> (</span><span>depMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>85</div></div><div><span><span>    </span></span><span>dep</span><span> </span><span>=</span><span><span> </span><span>depMap</span><span>.</span></span><span>get</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>86</div></div><div><span>    </span><span>// + 这里收集的时候,我们就需要来判断是否是否存在同样的fn了</span></div></div><div><div><div>87</div></div><div><span><span>    </span></span><span>([</span><span>...</span><span><span>dep</span><span>]).</span></span><span>every</span><span><span>((</span><span>f</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>f</span><span>.</span></span><span>fn</span><span> </span><span>!==</span><span><span> </span><span>activeEffect</span><span>.</span></span><span>fn</span><span>) </span><span>&amp;&amp;</span><span><span> </span><span>dep</span><span>.</span></span><span>add</span><span><span>(</span><span>activeEffect</span><span>)</span></span></div></div><div><div><div>88</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>89</div></div><div><span><span>    </span></span><span>depMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, (</span><span>dep</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span><span>([</span><span>activeEffect</span><span>])))</span></span></div></div><div><div><div>90</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>91</div></div><div><span>  </span><span>// 在这里将依赖集合给到副作用函数deps属性上</span></div></div><div><div><div>92</div></div><div><span><span>  </span></span><span>activeEffect</span><span>.</span><span>deps</span><span>.</span><span>push</span><span><span>(</span><span>dep</span><span>)</span></span></div></div><div><div><div>93</div></div><div><span>}</span></div></div><div><div><div>94</div></div><div><span>// 触发函数</span></div></div><div><div><div>95</div></div><div><span>const</span><span> </span><span>trigger</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>, </span><span>key</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>96</div></div><div><span>  </span><span>// 逻辑很简单,通过路径拿到属于自己的副作用函数集合,然后执行</span></div></div><div><div><div>97</div></div><div><span>  </span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>targetMap</span><span>.</span></span><span>get</span><span><span>(</span><span>target</span><span>)?.</span></span><span>get</span><span><span>(</span><span>key</span><span>) </span></span><span>||</span><span> []).</span><span>forEach</span><span><span>(</span><span>effect</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>98</div></div><div><span>    </span><span>if</span><span><span> (</span><span>effect</span><span>.</span></span><span>options</span><span>.</span><span>scheduler</span><span>) {</span></div></div><div><div><div>99</div></div><div><span>      </span><span>// 我们此时将副作用函数传递给用户自定义的调度器,让用户自己去决定是否执行</span></div></div><div><div><div>100</div></div><div><span><span>      </span></span><span>effect</span><span>.</span><span>options</span><span>.</span><span>scheduler</span><span><span>(</span><span>effect</span><span>)</span></span></div></div><div><div><div>101</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>102</div></div><div><span><span>      </span></span><span>effect</span><span> </span><span>!==</span><span><span> </span><span>activeEffect</span><span> </span></span><span>&amp;&amp;</span><span> </span><span>effect</span><span>()</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><span><span>  </span></span><span>});</span></div></div><div><div><div>105</div></div><div><span>}</span></div></div><div><div><div>106</div></div><div><span>// 最最核心的函数!</span></div></div><div><div><div>107</div></div><div><span>const</span><span> </span><span>effect</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>options</span><span> </span></span><span>=</span><span> {}) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>108</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>109</div></div><div><span><span>    </span></span><span>activeEffect</span><span> </span><span>=</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>110</div></div><div><span>    </span><span>// 清理</span></div></div><div><div><div>111</div></div><div><span>    </span><span>clean</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>112</div></div><div><span>    </span><span>// 在函数执行前,将函数推入栈中</span></div></div><div><div><div>113</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>push</span><span><span>(</span><span>effectFn</span><span>)</span></span></div></div><div><div><div>114</div></div><div><span>    </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>fn</span><span>()</span></div></div><div><div><div>115</div></div><div><span>    </span><span>// 执行完毕后,将函数推出栈</span></div></div><div><div><div>116</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>pop</span><span>()</span></div></div><div><div><div>117</div></div><div><span>    </span><span>// 将栈顶的函数赋值给activeEffect,如果栈中没有函数了,就不需要再改变activeEffect了</span></div></div><div><div><div>118</div></div><div><span><span>    </span></span><span>effectFnStack</span><span>.</span><span>length</span><span> </span><span>&amp;&amp;</span><span><span> (</span><span>activeEffect</span><span> </span></span><span>=</span><span><span> </span><span>effectFnStack</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>))</span></div></div><div><div><div>119</div></div><div><span>    </span><span>return</span><span><span> </span><span>res</span></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>// 在该函数身上声明一个属性,用来储存该函数所对应的依赖set</span></div></div><div><div><div>122</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>123</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>fn</span><span> </span><span>=</span><span><span> </span><span>fn</span></span></div></div><div><div><div>124</div></div><div><span><span>  </span></span><span>effectFn</span><span>.</span><span>options</span><span> </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>125</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>options</span><span>.</span></span><span>lazy</span><span>) {</span></div></div><div><div><div>126</div></div><div><span>    </span><span>effectFn</span><span>()</span></div></div><div><div><div>127</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>128</div></div><div><span>  </span><span>return</span><span><span> </span><span>effectFn</span></span></div></div><div><div><div>129</div></div><div><span>}</span></div></div><div><div><div>130</div></div><div><span>const</span><span> </span><span>clean</span><span> </span><span>=</span><span><span> (</span><span>effectFn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>131</div></div><div><span>  </span><span>const</span><span> </span><span>deps</span><span> </span><span>=</span><span><span> </span><span>effectFn</span><span>.</span></span><span>deps</span><span> </span><span>||</span><span> []</span></div></div><div><div><div>132</div></div><div><span><span>  </span></span><span>deps</span><span>.</span><span>forEach</span><span><span>(</span><span>dep</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>133</div></div><div><span><span>    </span></span><span>dep</span><span>.</span><span>delete</span><span><span>(</span><span>effectFn</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><span><span>  </span></span><span>effectFn</span><span>.</span><span>deps</span><span>.</span><span>length</span><span> </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>136</div></div><div><span>}</span></div></div><div><div><div>137</div></div><div><span>// 计算属性api</span></div></div><div><div><div>138</div></div><div><span>function</span><span> </span><span>computed</span><span><span>(</span><span>getter</span><span>) {</span></span></div></div><div><div><div>139</div></div><div><span>  </span><span>let</span><span><span> </span><span>value</span><span>, </span><span>dirty</span><span> </span></span><span>=</span><span> </span><span>true</span></div></div><div><div><div>140</div></div><div><span>  </span><span>const</span><span> </span><span>scheduler</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>141</div></div><div><span><span>    </span></span><span>dirty</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>142</div></div><div><span>    </span><span>trigger</span><span><span>(</span><span>obj</span><span>, </span></span><span>'value'</span><span>)</span></div></div><div><div><div>143</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>144</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span><span>(</span><span>getter</span><span>, { </span></span><span>lazy</span><span>:</span><span> </span><span>true</span><span><span>, </span><span>scheduler</span><span> })</span></span></div></div><div><div><div>145</div></div><div><span>  </span><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>146</div></div><div><span>    </span><span>get</span><span> </span><span>value</span><span>() {</span></div></div><div><div><div>147</div></div><div><span>      </span><span>if</span><span><span> (</span><span>dirty</span><span>) {</span></span></div></div><div><div><div>148</div></div><div><span><span>        </span></span><span>value</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>149</div></div><div><span><span>        </span></span><span>dirty</span><span> </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>150</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>151</div></div><div><span>      </span><span>track</span><span><span>(</span><span>obj</span><span>, </span></span><span>'value'</span><span>)</span></div></div><div><div><div>152</div></div><div><span>      </span><span>return</span><span><span> </span><span>value</span></span></div></div><div><div><div>153</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>154</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>155</div></div><div><span>  </span><span>return</span><span><span> </span><span>obj</span></span></div></div><div><div><div>156</div></div><div><span>}</span></div></div><div><div><div>157</div></div><div><span>// 这个函数主要用来访问一个对象下的所有后代属性</span></div></div><div><div><div>158</div></div><div><span>function</span><span> </span><span>traverse</span><span><span>(</span><span>value</span><span>, </span><span>seen</span><span> </span></span><span>=</span><span> </span><span>new</span><span><span> </span><span>Set</span><span>) {</span></span></div></div><div><div><div>159</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span>isObject</span><span><span>(</span><span>value</span><span>) </span></span><span>||</span><span><span> </span><span>seen</span><span>.</span></span><span>has</span><span><span>(</span><span>value</span><span>)) {</span></span></div></div><div><div><div>160</div></div><div><span>    </span><span>return</span><span><span> </span><span>value</span></span></div></div><div><div><div>161</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>162</div></div><div><span>  </span><span>// 这里是为了防止循环引用</span></div></div><div><div><div>163</div></div><div><span><span>  </span></span><span>seen</span><span>.</span><span>add</span><span><span>(</span><span>value</span><span>)</span></span></div></div><div><div><div>164</div></div><div><span>  </span><span>if</span><span><span> (</span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>value</span><span>)) {</span></span></div></div><div><div><div>165</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>value</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>166</div></div><div><span>      </span><span>traverse</span><span><span>(</span><span>value</span><span>[</span><span>i</span><span>], </span><span>seen</span><span>)</span></span></div></div><div><div><div>167</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>168</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>169</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>value</span><span>) {</span></span></div></div><div><div><div>170</div></div><div><span>      </span><span>traverse</span><span><span>(</span><span>value</span><span>[</span><span>key</span><span>], </span><span>seen</span><span>)</span></span></div></div><div><div><div>171</div></div><div><span><span>    </span></span><span>}</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>return</span><span><span> </span><span>value</span></span></div></div><div><div><div>174</div></div><div><span>}</span></div></div><div><div><div>175</div></div><div><span>// watch api</span></div></div><div><div><div>176</div></div><div><span>function</span><span> </span><span>watch</span><span><span>(</span><span>obj</span><span>, </span><span>cb</span><span>, </span><span>options</span><span> </span></span><span>=</span><span> {}) {</span></div></div><div><div><div>177</div></div><div><span>  </span><span>let</span><span><span> </span><span>newVal</span><span>, </span><span>oldVal</span></span></div></div><div><div><div>178</div></div><div><span>  </span><span>const</span><span> { </span><span>flush</span><span> </span><span>=</span><span> </span><span>'pre'</span><span>, </span><span>immediate</span><span> </span><span>=</span><span> </span><span>false</span><span> } </span><span>=</span><span><span> </span><span>options</span></span></div></div><div><div><div>179</div></div><div><span>  </span><span>const</span><span> </span><span>cleanups</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>()</span></div></div><div><div><div>180</div></div><div><span>  </span><span>// 注册清理副作用函数</span></div></div><div><div><div>181</div></div><div><span>  </span><span>const</span><span> </span><span>onInvalidate</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>182</div></div><div><span><span>    </span></span><span>cleanups</span><span>.</span><span>add</span><span><span>(</span><span>fn</span><span>)</span></span></div></div><div><div><div>183</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>184</div></div><div><span>  </span><span>const</span><span> </span><span>job</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>185</div></div><div><span><span>    </span></span><span>newVal</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>186</div></div><div><span>    </span><span>// 每次执行回调之前,先执行清理函数,避免执行过期的副作用函数</span></div></div><div><div><div>187</div></div><div><span><span>    </span></span><span>cleanups</span><span>.</span><span>forEach</span><span><span>(</span><span>cleanup</span><span> </span></span><span>=&gt;</span><span> </span><span>cleanup</span><span>())</span></div></div><div><div><div>188</div></div><div><span>    </span><span>// 把onInvalidate函数传递给回调函数</span></div></div><div><div><div>189</div></div><div><span>    </span><span>cb</span><span><span>(</span><span>newVal</span><span>, </span><span>oldVal</span><span>, </span><span>onInvalidate</span><span>)</span></span></div></div><div><div><div>190</div></div><div><span>    </span><span>// 更新旧值</span></div></div><div><div><div>191</div></div><div><span><span>    </span></span><span>oldVal</span><span> </span><span>=</span><span><span> </span><span>newVal</span></span></div></div><div><div><div>192</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>193</div></div><div><span>  </span><span>const</span><span> </span><span>isGetter</span><span> </span><span>=</span><span> </span><span>isFunction</span><span><span>(</span><span>obj</span><span>)</span></span></div></div><div><div><div>194</div></div><div><span>  </span><span>const</span><span> </span><span>jobFlushMap</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>195</div></div><div><span>    </span><span>// 这是watch的默认行为,需要新的调度器,用来保证在vue更新前一步执行,会涉及到组件的更新周期钩子问题,这里我们就不做了</span></div></div><div><div><div>196</div></div><div><span>    </span><span>pre</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>job</span><span>(),</span></div></div><div><div><div>197</div></div><div><span>    </span><span>// 这里也是一样的,需要使用不同的调度器来保证在vue更新完成后进行调用,这里我们使用前边创建的queueJob来模拟</span></div></div><div><div><div>198</div></div><div><span>    </span><span>post</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>queueJob</span><span><span>(</span><span>job</span><span>),</span></span></div></div><div><div><div>199</div></div><div><span>    </span><span>// 直接执行,不需要任何调度器</span></div></div><div><div><div>200</div></div><div><span>    </span><span>sync</span><span>:</span><span><span> </span><span>job</span></span></div></div><div><div><div>201</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>202</div></div><div><span>  </span><span>const</span><span> </span><span>effectFn</span><span> </span><span>=</span><span> </span><span>effect</span><span>(</span></div></div><div><div><div>203</div></div><div><span><span>    </span></span><span>isGetter</span><span> </span><span>?</span><span><span> </span><span>obj</span><span> </span></span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>traverse</span><span><span>(</span><span>obj</span><span>),</span></span></div></div><div><div><div>204</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>205</div></div><div><span>      </span><span>lazy</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>206</div></div><div><span>      </span><span>scheduler</span><span>:</span><span><span> </span><span>jobFlushMap</span><span>[</span><span>flush</span><span>] </span></span><span>||</span><span><span> </span><span>job</span></span></div></div><div><div><div>207</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>208</div></div><div><span>  </span><span>// 是否是立即执行</span></div></div><div><div><div>209</div></div><div><span>  </span><span>if</span><span><span> (</span><span>immediate</span><span>) {</span></span></div></div><div><div><div>210</div></div><div><span>    </span><span>job</span><span>()</span></div></div><div><div><div>211</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>212</div></div><div><span>    </span><span>// 获取到初始值</span></div></div><div><div><div>213</div></div><div><span><span>    </span></span><span>oldVal</span><span> </span><span>=</span><span> </span><span>effectFn</span><span>()</span></div></div><div><div><div>214</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>215</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><p>结束goodbye</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/qian-lai-ling-jiao-yi-xia-han-shu-shi-bian-cheng-de-mei-li/</id>
      <title type="text">前来领教一下函数式编程的魅力</title>
      <published>2023-04-15T15:52:06.000Z</published>
      <updated>2023-04-15T15:52:06.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/qian-lai-ling-jiao-yi-xia-han-shu-shi-bian-cheng-de-mei-li/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么我们需要函数式编程?<a href="#为什么我们需要函数式编程"><span>#</span></a></h2><p>可能大家已经听说函数式编程带来的各种好处,其中最吸引人的莫过于它可以降低代码的复杂度,这一点真的太重要了,实现同样的功能,面向对象的代码会更复杂,而使用函数式编程则可以轻松控制复杂度,那我们就没有理由不选择函数式编程啦!
注意,函数式编程不是一种解决方案,它是一种编程范式,如果你已经熟悉了面向对象编程这种思维习惯,那么你学习函数式编程则需要转变你的思维模式,初期可能会很困难,但是这都没关系,慢慢就会熟悉的.</p></section>
<section><h2>在真正开始之前,我们首先要知道的几个概念<a href="#在真正开始之前我们首先要知道的几个概念"><span>#</span></a></h2><p>我们先来理解在函数式编程中几个常见的概念吧</p><section><h3>纯函数<a href="#纯函数"><span>#</span></a></h3><p>纯函数的具有以下特点</p><ul>
<li>相同的输入总是返回相同的输出，即函数的输出只依赖于它的输入参数。</li>
<li>函数没有副作用，即它不会对除了它自己的外部环境产生任何影响。</li>
</ul></section><section><h3>副作用函数<a href="#副作用函数"><span>#</span></a></h3><p>副作用函数具有包括但不限于以下特点</p><ul>
<li>修改全局变量或静态变量</li>
<li>修改函数参数</li>
<li>抛出异常</li>
<li>修改文件系统</li>
<li>发送网络请求</li>
<li>调用其他副作用函数</li>
</ul></section><section><h3>柯里化<a href="#柯里化"><span>#</span></a></h3><p>柯里化（Currying）是指将一个多参数的函数转换成一系列单参数的函数的过程。换句话说，柯里化是一种将函数套娃的技术，它通过返回一个新函数来实现。新函数接受第一个参数并返回一个接受第二个参数的函数，以此类推，直到所有参数都被传递完毕，最终返回最终结果。</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>function</span><span> </span><span>add</span><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>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span> </span></span><span>+</span><span><span> </span><span>z</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>// 柯里化后的函数</span></div></div><div><div><div>7</div></div><div><span>function</span><span> </span><span>curriedAdd</span><span><span>(</span><span>x</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>  </span><span>return</span><span> </span><span>function</span><span><span>(</span><span>y</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>return</span><span> </span><span>function</span><span><span>(</span><span>z</span><span>) {</span></span></div></div><div><div><div>10</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><span>+</span><span><span> </span><span>z</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>}</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>curriedAdd</span><span>(</span><span>1</span><span>)(</span><span>2</span><span>)(</span><span>3</span><span>); </span><span>// 6</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>// 原始函数</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>add</span><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>return</span><span><span> </span><span>x</span><span> </span></span><span>+</span><span><span> </span><span>y</span><span> </span></span><span>+</span><span><span> </span><span>z</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>// 偏函数</span></div></div><div><div><div>7</div></div><div><span>const</span><span> </span><span>addPartial</span><span> </span><span>=</span><span><span> </span><span>x</span><span> </span></span><span>=&gt;</span><span><span> (</span><span>y</span><span>, </span><span>z</span><span>) </span></span><span>=&gt;</span><span> </span><span>add</span><span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>z</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 使用偏函数</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>add5</span><span> </span><span>=</span><span> </span><span>addPartial</span><span>(</span><span>5</span><span>);</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>add5</span><span>(</span><span>2</span><span>, </span><span>3</span><span>); </span><span>// 10</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>引用透明性<a href="#引用透明性"><span>#</span></a></h3><p>引用透明性是指一个表达式可以被它的值替换而不影响程序的行为。换句话说，引用透明性是指一个函数的输出仅取决于其输入，而与函数的内部状态、副作用或上下文无关。</p><p>例如，考虑以下函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>add</span><span><span>(</span><span>x</span><span>, </span><span>y</span><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>这个函数是引用透明的，因为对于任何给定的x和y，它的输出总是x和y的和。我们可以将这个函数的调用替换为它的值，而不会影响程序的行为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>add</span><span>(</span><span>2</span><span>, </span><span>3</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>result2</span><span> </span><span>=</span><span> </span><span>5</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>可置换性是指在程序中可以用一个表达式替换另一个表达式而不会影响程序的行为。</p><p>例如，考虑以下代码片段：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>javascript</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>x</span><span> </span><span>=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>y</span><span> </span><span>=</span><span> </span><span>3</span><span>;</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>z</span><span> </span><span>=</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><p>在这个代码片段中，我们可以将x + y替换为它的值5，而不会影响程序的行为：</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>=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>y</span><span> </span><span>=</span><span> </span><span>3</span><span>;</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>z</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>这是因为x + y是可置换的，它的值可以被它的计算结果替换，而不会影响程序的行为。</p></section><section><h3>不可变数据<a href="#不可变数据"><span>#</span></a></h3><p>不可变数据是指一旦创建就不能被修改的数据。这意味着一旦数据被创建，它的值就不能被改变，而任何对该数据的修改都会创建一个新的数据。</p><p>不可变数据是函数式编程的一个重要概念，因为它可以确保程序的正确性和可预测性。由于不可变数据不能被修改，所以它们可以被共享和缓存，从而提高程序的性能和效率。此外，不可变数据还可以避免出现副作用，从而使得程序更加易于理解和测试。</p><p>在js中,基础类型天生就具有不可变的特点,但是在引用类型上是可以被后期改变的,这会带来副作用,我们需要有意识的取避免这种情况发生，通常我们可以有一些手段来规避这些问题：</p><ul>
<li>闭包来隐藏变量，只向外界暴露get之类的操作，而没有set之类的方法，这样就可以做到不可变性了。虽然这样可以做到，但是由于js的动态性，这些暴露的方法也可以被重新覆写，唉，这也是没有办法的事情。</li>
<li>还有一种保证引用类型不可变性的方式，就是完全拷贝一份对象的副本，这样即使你如何操作副本，源对象也不会受到影响。</li>
<li>还可以通过代理源对象的行为来控制，比如使用Proxy，definedProperty来劫持set行为，使其触及不到源对象，比如使用Object.freeze()来冻结对象，它内部使用的也是definedProperty来重写属性的描述符达到不可更改的目的.</li>
<li>社区中其实已经有一些专门的库来解决这些问题,比如ramda.js,它可以让你在做写入操作时,只会产生一个局部的副本,而不是创建完整的源对象本身,这样的性能无疑会比你每次都完整克隆一个源对象来得高得多</li>
</ul></section></section>
<section><h2>下面我们来展示一些实际的代码来提示函数式编程的好处<a href="#下面我们来展示一些实际的代码来提示函数式编程的好处"><span>#</span></a></h2><p>函数式编程宏观上来说是鼓励任务的拆分,让子任务之间相互独立遵循单一原则,然后通过组合来完成最终的任务;组合和拆分,他们之间的相互作用就促成了函数式编程范式.</p><p>以下是一些使用函数式编程的代码示例，感受一下函数式编程带来的优点：</p><section><h3>易于测试<a href="#易于测试"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>add</span><span><span>(</span><span>x</span><span>, </span><span>y</span><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>
</div></div><div><div><div>5</div></div><div><span>// 测试</span></div></div><div><div><div>6</div></div><div><span><span>assert</span><span>.</span></span><span>equal</span><span>(</span><span>add</span><span>(</span><span>2</span><span>, </span><span>3</span><span>), </span><span>5</span><span>);</span></div></div><div><div><div>7</div></div><div><span><span>assert</span><span>.</span></span><span>equal</span><span>(</span><span>add</span><span>(</span><span>5</span><span>, </span><span>7</span><span>), </span><span>12</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>由于add函数是纯函数，它只依赖于输入参数，不会对外部环境造成影响。这使得测试变得容易，并且我们可以轻松地测试它的输出，而不需要考虑测试环境的状态。</p></section><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>numbers</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><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></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>sum</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>5</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>numbers</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>sum</span><span> </span><span>+=</span><span><span> </span><span>numbers</span><span>[</span><span>i</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></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>sum</span><span> </span><span>=</span><span><span> </span><span>numbers</span><span>.</span></span><span>reduce</span><span><span>((</span><span>acc</span><span>, </span><span>cur</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>acc</span><span> </span></span><span>+</span><span><span> </span><span>cur</span><span>, </span></span><span>0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在这个例子中，我们可以看到函数式代码更简洁、更易读。reduce函数的目的很明显，它的实现描述了我们正在做什么。</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>let</span><span><span> </span><span>count</span><span> </span></span><span>=</span><span> </span><span>0</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>numbers</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span><span> (</span><span>numbers</span><span>[</span><span>i</span><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>count</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><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 函数式</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>count</span><span> </span><span>=</span><span><span> </span><span>numbers</span><span>.</span></span><span>filter</span><span><span>(</span><span>num</span><span> </span></span><span>=&gt;</span><span><span> </span><span>num</span><span> </span></span><span>%</span><span> </span><span>2</span><span> </span><span>===</span><span> </span><span>0</span><span>).</span><span>length</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在这个例子中，函数式代码更简洁、更易读，更容易维护。我们不需要担心副作用，因为我们只是从数组中筛选出偶数，并计算它们的数量。</p></section><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>numbers</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><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></div></div><div><div><div>4</div></div><div><span>let</span><span><span> </span><span>squares</span><span> </span></span><span>=</span><span> [];</span></div></div><div><div><div>5</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>numbers</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>squares</span><span>.</span><span>push</span><span><span>(</span><span>numbers</span><span>[</span><span>i</span><span>] </span></span><span>*</span><span><span> </span><span>numbers</span><span>[</span><span>i</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></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>squares</span><span> </span><span>=</span><span><span> </span><span>numbers</span><span>.</span></span><span>map</span><span><span>(</span><span>num</span><span> </span></span><span>=&gt;</span><span><span> </span><span>num</span><span> </span></span><span>*</span><span><span> </span><span>num</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在这个例子中，我们可以看到函数式代码更简洁、更易读，更容易并行处理。由于map函数是纯函数，它可以安全地在多个线程或进程中同时运行，而不会产生竞态条件和死锁。</p></section></section>
<section><h2>函数式编程和面向对象编程的区别<a href="#函数式编程和面向对象编程的区别"><span>#</span></a></h2><p>函数式编程和面向对象编程是两种不同的编程范式，它们有一些重要的区别：</p><ul>
<li>
<ol>
<li>数据和操作的处理方式不同：</li>
</ol>
</li>
</ul><p>在面向对象编程中，数据和操作是紧密耦合的。对象是数据和操作的集合，数据和操作都封装在对象中。对象之间通过方法调用来交互和通信。</p><p>而在函数式编程中，数据和操作是分离的。数据和操作被视为两个独立的实体，函数接受数据作为输入并返回输出。函数之间通过参数和返回值来交互和通信。</p><ul>
<li>
<ol>
<li>可变性不同：</li>
</ol>
</li>
</ul><p>在面向对象编程中，对象的状态可以被修改，因此对象是可变的。这意味着对象的状态可以在程序执行过程中发生变化。</p><p>而在函数式编程中，数据是不可变的。一旦数据被创建，它的值就不能被修改。这样可以避免副作用，并使程序更加可靠和容易理解。</p><ul>
<li>
<ol>
<li>控制流不同：</li>
</ol>
</li>
</ul><p>在面向对象编程中，程序的控制流通常是通过方法调用和对象之间的交互来实现的。这种方式可以使程序的控制流更加灵活和动态。</p><p>而在函数式编程中，程序的控制流通常是通过函数的调用链来实现的。这种方式可以使程序的控制流更加静态和预测。</p><ul>
<li>
<ol>
<li>并发性不同：</li>
</ol>
</li>
</ul><p>函数式编程更适合并发编程，因为函数不会修改状态，所以它们可以更容易地并行执行。</p><p>而面向对象编程更适合串行编程，因为对象之间的状态共享使得并发执行更加困难。</p></section>
<section><h2>好的步入正题&lt;函数式基础&gt;<a href="#好的步入正题"><span>#</span></a></h2><p>接下来,正式步入函数式编程,一步一步走近函数式编程!</p><section><h3>理解程序控制流<a href="#理解程序控制流"><span>#</span></a></h3><p>控制流（Control Flow）可以理解你的程序为了实现业务需求而所行进的路径。</p><p>在面向对象编程里,我们通常需要暴露所有的的必要步骤才能很好的描述控制流,但是在函数式编程中,通过语义化的函数调用,并使用某些手段将其链接起来,可以很清晰的描述控制流.</p></section><section><h3>链式调用与管道函数<a href="#链式调用与管道函数"><span>#</span></a></h3><p>在我们将大模块分化成一个又一个函数的时候,如何将这些函数联系起来成了一个问题,主流的方法是链式调用或者使用管道函数,而且中链式调用的限制会让我们更倾向于使用管道函数</p><p>下边是一个lodash中函数式编程链式调用的示例</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// lodash 链式调用示例</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>_</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'lodash'</span><span>)</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>nums</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><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><span>10</span><span>]</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>newNums</span><span> </span><span>=</span><span><span> </span><span>_</span><span>.</span></span><span>chain</span><span><span>(</span><span>nums</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>.</span><span>map</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>6</div></div><div><span><span>  </span></span><span>.</span><span>filter</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>2</span><span> </span><span>===</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>.</span><span>value</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>newNums</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这里会有一个问题是,我们必须使用lodash提供给我们的对象,哪怕你自己实现一个自己的链式对象,你还是需要遵循指定的规则,而使用管道函数不会有这个问题,它只需要你满足两边两个条件即可使用:</p><ul>
<li>类型&lt;管道中的函数返回值必须与接受函数的参数类型一致&gt;</li>
<li>元数(函数参数定义的个数)&lt;接受函数必须声明至少一个参数来接受上一个函数的返回值&gt;</li>
</ul><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>const</span><span> </span><span>pipe</span><span> </span><span>=</span><span> (</span><span>...</span><span><span>fns</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>x</span><span> </span></span><span>=&gt;</span><span><span> </span><span>fns</span><span>.</span></span><span>reduce</span><span><span>((</span><span>v</span><span>, </span><span>f</span><span>) </span></span><span>=&gt;</span><span> </span><span>f</span><span><span>(</span><span>v</span><span>), </span><span>x</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>newNums2</span><span> </span><span>=</span><span> </span><span>pipe</span><span>(</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>n</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>5</div></div><div><span><span>  </span></span><span>n</span><span> </span><span>=&gt;</span><span><span> </span><span>n</span><span> </span></span><span>*</span><span> </span><span>2</span><span>,</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>n</span><span> </span><span>=&gt;</span><span><span> </span><span>n</span><span> </span></span><span>/</span><span> </span><span>3</span></div></div><div><div><div>7</div></div><div><span>)(</span><span>1</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>newNums2</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>函数柯里化（Currying）是一种将接受多个参数的函数转换为一系列接受单个参数的函数的技术。
举个例子，对于一个接受两个参数的函数 f(x, y)，使用柯里化后可以得到一个新的函数 g(x)，它返回一个接受单个参数的函数 h(y)，调用 g(x) 后再调用 h(y) 等价于直接调用 f(x, y)。
具体来说，柯里化是通过闭包实现的，每次调用柯里化函数时，都会返回一个新的函数，这个新函数会将之前传入的参数保存起来，并返回一个接受下一个参数的函数，直到所有参数都传入为止，最后再执行原始函数并返回结果。
函数柯里化的好处在于可以将一个接受多个参数的函数转化为多个只接受单个参数的函数，这样可以更加灵活地组合和复用函数，同时也可以更方便地进行函数的部分应用和参数绑定。
总之，函数柯里化是一种非常有用的函数式编程技术，可以帮助我们更好地进行函数组合和复用，提高代码的可读性和可维护性。</p><p>一下是一段函数柯里化的示例代码,我们想要获取三个数的求和</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>creater</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> </span><span>function</span><span><span> (</span><span>first</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span> </span><span>function</span><span><span> (</span><span>second</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>      </span><span>return</span><span> </span><span>function</span><span><span> (</span><span>third</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>        </span><span>return</span><span> </span><span>fn</span><span><span>(</span><span>first</span><span>, </span><span>second</span><span>, </span><span>third</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></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>sum</span><span> </span><span>=</span><span> </span><span>creater</span><span><span>((</span><span>first</span><span>, </span><span>second</span><span>, </span><span>third</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span><span> </span><span>first</span><span> </span></span><span>+</span><span><span> </span><span>second</span><span> </span></span><span>+</span><span><span> </span><span>third</span></span></div></div><div><div><div>12</div></div><div><span>})</span></div></div><div><div><div>13</div></div><div><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><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>14</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>res</span><span>); </span></span><span>//6</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>arr</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><span>'5'</span><span>, </span><span>'6'</span><span>]</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>nums</span><span> </span><span>=</span><span><span> </span><span>arr</span><span>.</span></span><span>map</span><span><span>(</span><span>parseInt</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>// 使用柯里化来限制parseInt只接受第一个参数</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>curry</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>return</span><span><span> (</span><span>a</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span> </span><span>fn</span><span><span>(</span><span>a</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>const</span><span> </span><span>nums2</span><span> </span><span>=</span><span><span> </span><span>arr</span><span>.</span></span><span>map</span><span>(</span><span>curry</span><span><span>(</span><span>parseInt</span><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>nums</span><span>); </span></span><span>// [ 1, NaN, NaN, NaN, NaN, NaN ]</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>nums2</span><span>); </span></span><span>// [ 1, 2, 3, 4, 5, 6 ]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>部分应用<a href="#部分应用"><span>#</span></a></h3><p>部分应用（Partial Application）是函数式编程中的一个概念，它指的是将一个多参数函数转换为一个接受较少参数的函数的过程。在 TypeScript 中，您可以使用函数重载和箭头函数来实现部分应用。</p><p>以下是一个示例，演示如何使用箭头函数和部分应用来创建新的函数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 定义一个名为 "add" 的函数</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>add</span><span><span>(</span><span>a</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>b</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>c</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>3</div></div><div><span>  </span><span>return</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span><span> </span></span><span>+</span><span><span> </span><span>c</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>// 使用箭头函数和部分应用来创建新的函数</span></div></div><div><div><div>7</div></div><div><span>const</span><span> </span><span>addOne</span><span> </span><span>=</span><span><span> (</span><span>a</span></span><span>:</span><span> </span><span>number</span><span>) </span><span>=&gt;</span><span><span> (</span><span>b</span></span><span>:</span><span> </span><span>number</span><span><span>, </span><span>c</span></span><span>:</span><span> </span><span>number</span><span>) </span><span>=&gt;</span><span> </span><span>add</span><span><span>(</span><span>a</span><span>, </span><span>b</span><span>, </span><span>c</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 调用新的函数</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>addOne</span><span>(</span><span>1</span><span>)(</span><span>2</span><span>, </span><span>3</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>result</span><span>); </span></span><span>// 输出：6</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在上面的示例中，我们首先定义了一个名为 add 的函数，它接受三个数字类型的参数，并返回它们的和。</p><p>然后，我们使用箭头函数和部分应用来创建一个新的函数 addOne。在箭头函数中，我们首先传递一个数字类型的参数 a，然后返回一个新的函数，该函数接受两个数字类型的参数 b 和 c，并将它们与 a 一起传递给原始函数 add。这样，我们就将一个多参数函数转换为了一个接受较少参数的函数。</p><p>最后，我们调用新的函数 addOne，并传递 1、2 和 3 三个数字作为参数。在函数调用中，我们可以使用箭头函数语法来将多个参数分开传递给函数，并使用部分应用来创建新的函数。</p></section></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>function</span><span> </span><span>restArguments</span><span><span>(</span><span>func</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> </span><span>function</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>// startIndex 表示使用哪个位置的参数用于储存剩余的参数</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>var</span><span><span> </span><span>startIndex</span><span> </span></span><span>=</span><span><span> </span><span>func</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>var</span><span><span> </span><span>length</span><span> </span></span><span>=</span><span> </span><span>arguments</span><span>.</span><span>length</span><span> </span><span>-</span><span><span> </span><span>startIndex</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>rest</span><span> </span><span>=</span><span> </span><span>Array</span><span><span>(</span><span>length</span><span>),</span></span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>index</span><span> </span><span>=</span><span> </span><span>0</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>// 以上面例子为例，rest结果为[3, 4, 5]</span></div></div><div><div><div>11</div></div><div><span>    </span><span>for</span><span><span> (; </span><span>index</span><span> </span></span><span>&lt;</span><span><span> </span><span>length</span><span>; </span><span>index</span></span><span>++</span><span>) {</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>rest</span><span>[</span><span>index</span><span>] </span><span>=</span><span> </span><span>arguments</span><span><span>[</span><span>index</span><span> </span></span><span>+</span><span><span> </span><span>startIndex</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>var</span><span><span> </span><span>args</span><span> </span></span><span>=</span><span> </span><span>Array</span><span><span>(</span><span>startIndex</span><span> </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>// args此时为[1, 2, undefined]</span></div></div><div><div><div>16</div></div><div><span>    </span><span>for</span><span><span> (</span><span>index</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>index</span><span> </span></span><span>&lt;</span><span><span> </span><span>startIndex</span><span>; </span><span>index</span></span><span>++</span><span>) {</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>args</span><span>[</span><span>index</span><span>] </span><span>=</span><span> </span><span>arguments</span><span><span>[</span><span>index</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>// args此时为[1, 2, [3, 4, 5]]</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>args</span><span>[</span><span>startIndex</span><span>] </span><span>=</span><span><span> </span><span>rest</span><span>;</span></span></div></div><div><div><div>21</div></div><div><span>    </span><span>return</span><span><span> </span><span>func</span><span>.</span></span><span>apply</span><span>(</span><span>this</span><span><span>, </span><span>args</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>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/shi-yong-ts-vue3-element-plus-feng-zhuang-yi-ge-ming-ling-shi-tan-chuang-zu-jian/</id>
      <title type="text">使用TS+Vue3+element-plus封装一个命令式弹窗组件</title>
      <published>2023-03-29T22:23:41.000Z</published>
      <updated>2023-03-29T22:23:41.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/shi-yong-ts-vue3-element-plus-feng-zhuang-yi-ge-ming-ling-shi-tan-chuang-zu-jian/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么要封装一个命令式弹窗?<a href="#为什么要封装一个命令式弹窗"><span>#</span></a></h2><p>由于在项目中有很多弹窗,但是每次开发声明式弹窗内容的时候,内心总会有一种抵触感,因为在开发弹窗的时候给人一种很强的割裂感,如果你的弹窗内的内容是放在单独的组件内,就会有一种反复横跳的感觉,还要单独去关注维护弹窗显隐的状态,以及一些额外的操作。于是就萌生了要开发一个命令式弹窗组件的想法，在使用体验过后，尽管尤大大是反对命令式这种交互 》 <a href="https://www.zhihu.com/question/35820643/answer/64646527" target="_blank">链接</a>，但是我觉得都不是问题，用完真的回不去了。</p></section>
<section><h2>那么，该如何开始呢？<a href="#那么该如何开始呢"><span>#</span></a></h2><p>相信你们应该有使用过elementui的messageBox组件，他就是典型的命令式弹窗，而且也可以支持自定义内容，他这种交互方式不正是我们需要的吗？那么我们可以借鉴一下它是如何实现的。这里就不去一一细看它的源代码了，大致说一下它的实现思路</p><ul>
<li>创建组件容器</li>
<li>在容器内将自定义组件创建为一个Vnode</li>
<li>将容器挂载到挂载点</li>
<li>执行后续操作</li>
</ul></section>
<section><h2>于是<a href="#于是"><span>#</span></a></h2><p>我仿照messageBox实现了一个，以下是完整代码</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span><span> { </span><span>app</span><span> </span><span>as</span><span> </span></span><span>mainApp</span><span> } </span><span>from</span><span> </span><span>"@/main"</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>DialogProps</span><span>, </span><span>ElDialog</span><span> } </span><span>from</span><span> </span><span>"element-plus"</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>h</span><span>, </span><span>InjectionKey</span><span>, </span><span>nextTick</span><span>, </span><span>provide</span><span>, </span><span>ref</span><span>, </span><span>render</span><span>, </span><span>VNode</span><span> } </span><span>from</span><span> </span><span>"vue"</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>const</span><span> </span><span>HideInject</span><span>:</span><span> </span><span>InjectionKey</span><span>&lt;() </span><span>=&gt;</span><span><span> </span><span>void</span><span>&gt; </span></span><span>=</span><span> </span><span>Symbol</span><span>(</span><span>"CommandDialogHide"</span><span>);</span></div></div><div><div><div>6</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>ShowInject</span><span>:</span><span> </span><span>InjectionKey</span><span>&lt;() </span><span>=&gt;</span><span><span> </span><span>void</span><span>&gt; </span></span><span>=</span><span> </span><span>Symbol</span><span>(</span><span>"CommandDialogShow"</span><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>const</span><span> </span><span>DestroyInject</span><span>:</span><span> </span><span>InjectionKey</span><span>&lt;() </span><span>=&gt;</span><span><span> </span><span>void</span><span>&gt; </span></span><span>=</span><span> </span><span>Symbol</span><span>(</span><span>"CommandDialogDestroy"</span><span>);</span></div></div><div><div><div>9</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>ResolveDestroyInject</span><span>:</span><span> </span><span>InjectionKey</span><span><span>&lt;(</span><span>data</span></span><span><span>?</span><span>:</span></span><span><span> </span><span>any</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>void</span><span>&gt; </span></span><span>=</span><span> </span><span>Symbol</span><span>(</span><span>"CommandDialogPromiseResolve"</span><span>);</span></div></div><div><div><div>10</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>RejectDestroyInject</span><span>:</span><span> </span><span>InjectionKey</span><span><span>&lt;(</span><span>err</span></span><span><span>?</span><span>:</span></span><span><span> </span><span>any</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>void</span><span>&gt; </span></span><span>=</span><span> </span><span>Symbol</span><span>(</span><span>"CommandDialogPromiseReject"</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// TODO 嵌套式弹窗还未实现,后续用到再说~</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>if</span><span> (</span><span>import</span><span>.</span><span>meta</span><span>.</span><span>env</span><span>.</span><span>DEV</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>  </span><span>const</span><span> </span><span>errorLog</span><span> </span><span>=</span><span><span> </span><span>console</span><span>.</span></span><span>error</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>error</span><span> </span><span>=</span><span> (</span><span>...</span><span><span>args</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span><span> (</span><span>args</span><span>[</span></span><span>0</span><span>].</span><span>toString</span><span>().</span><span>includes</span><span>(</span><span>`TypeError: Cannot read properties of null (reading 'nextSibling')`</span><span>)) {</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>window</span><span>.</span><span>location</span><span>.</span><span>reload</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>errorLog</span><span>(</span><span>...</span><span><span>args</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><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>// 弹窗默认属性</span></div></div><div><div><div>26</div></div><div><span>const</span><span> </span><span>defaultDialogProps</span><span> </span><span>=</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>draggable</span><span>:</span><span> </span><span>false</span><span>,</span></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>closeOnClickModal</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>31</div></div><div><span>  </span><span>// 弹出时body锁定</span></div></div><div><div><div>32</div></div><div><span>  </span><span>lockScroll</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>33</div></div><div><span>};</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>const</span><span> </span><span>visible</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>true</span><span>);</span></div></div><div><div><div>36</div></div><div><span>const</span><span> </span><span>hideFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>visible</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>38</div></div><div><span>};</span></div></div><div><div><div>39</div></div><div><span>const</span><span> </span><span>showFn</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>visible</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>41</div></div><div><span>};</span></div></div><div><div><div>42</div></div><div><span>const</span><span> </span><span>unmount</span><span> </span><span>=</span><span><span> (</span><span>container</span></span><span>:</span><span> </span><span>HTMLElement</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>43</div></div><div><span>  </span><span>hideFn</span><span>();</span></div></div><div><div><div>44</div></div><div><span>  </span><span>render</span><span>(</span><span>null</span><span><span>, </span><span>container</span><span>);</span></span></div></div><div><div><div>45</div></div><div><span>};</span></div></div><div><div><div>46</div></div><div><span>const</span><span> </span><span>destroyContainer</span><span> </span><span>=</span><span><span> (</span><span>container</span></span><span>:</span><span> </span><span>HTMLElement</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>47</div></div><div><span>  </span><span>unmount</span><span><span>(</span><span>container</span><span>);</span></span></div></div><div><div><div>48</div></div><div><span>  </span><span>nextTick</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>container</span><span>.</span><span>remove</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></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>export</span><span> </span><span>default</span><span> (</span></div></div><div><div><div>54</div></div><div><span>  </span><span>CustomComponent</span><span>: </span><span>VNode</span><span>,</span></div></div><div><div><div>55</div></div><div><span>  </span><span>options</span><span>: </span><span>Partial</span><span>&lt;</span><span>DialogProps</span><span> </span><span>&amp;</span><span> { </span><span>isProvideAppContext</span><span>:</span><span> </span><span>boolean</span><span> }</span><span>&gt;</span><span> </span><span>=</span><span> { </span><span>isProvideAppContext</span><span>:</span><span> </span><span>false</span><span> }</span></div></div><div><div><div>56</div></div><div><span>)</span><span>:</span><span> </span><span>Promise</span><span><span>&lt;</span><span>any</span><span>&gt; </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>57</div></div><div><span><span>  </span></span><span>visible</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>58</div></div><div><span>  </span><span>const</span><span> { </span><span>isProvideAppContext</span><span> </span><span>=</span><span> </span><span>true</span><span>, </span><span>...</span><span>dialogOptions</span><span> } </span><span>=</span><span> </span><span>options</span><span>;</span></div></div><div><div><div>59</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span>(</span><span>async</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>60</div></div><div><span>    </span><span>// 创建容器</span></div></div><div><div><div>61</div></div><div><span>    </span><span>const</span><span> </span><span>container</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>"div"</span><span>);</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>// 单纯调用这个方法会导致promise的状态永远为pending,外部组件将无法获得后续代码的执行权</span></div></div><div><div><div>64</div></div><div><span>    </span><span>const</span><span> </span><span>destroy</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>65</div></div><div><span>      </span><span>destroyContainer</span><span>(</span><span>container</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>
</div></div><div><div><div>68</div></div><div><span>    </span><span>const</span><span> </span><span>destroyOnResolve</span><span> </span><span>=</span><span><span> (</span><span>data</span></span><span>:</span><span> </span><span>any</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>69</div></div><div><span>      </span><span>destroy</span><span>();</span></div></div><div><div><div>70</div></div><div><span>      </span><span>resolve</span><span>(</span><span>data</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><span>const</span><span> </span><span>destroyOnReject</span><span> </span><span>=</span><span><span> (</span><span>reason</span></span><span>:</span><span> </span><span>any</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>73</div></div><div><span>      </span><span>destroy</span><span>();</span></div></div><div><div><div>74</div></div><div><span>      </span><span>reject</span><span>(</span><span>reason</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>
</div></div><div><div><div>77</div></div><div><span>    </span><span>// 弹窗主体</span></div></div><div><div><div>78</div></div><div><span>    </span><span>const</span><span> </span><span>vnode</span><span> </span><span>=</span><span> </span><span>h</span><span>({</span></div></div><div><div><div>79</div></div><div><span>      </span><span>setup</span><span>() {</span></div></div><div><div><div>80</div></div><div><span>        </span><span>// 将方法注入到子孙组件内</span></div></div><div><div><div>81</div></div><div><span>        </span><span>provide</span><span>(</span><span>ShowInject</span><span>, </span><span>showFn</span><span>);</span></div></div><div><div><div>82</div></div><div><span>        </span><span>provide</span><span>(</span><span>HideInject</span><span>, </span><span>hideFn</span><span>);</span></div></div><div><div><div>83</div></div><div><span>        </span><span>provide</span><span>(</span><span>DestroyInject</span><span>, </span><span>destroy</span><span>);</span></div></div><div><div><div>84</div></div><div><span>        </span><span>provide</span><span>(</span><span>ResolveDestroyInject</span><span>, </span><span>destroyOnResolve</span><span>);</span></div></div><div><div><div>85</div></div><div><span>        </span><span>provide</span><span>(</span><span>RejectDestroyInject</span><span>, </span><span>destroyOnReject</span><span>);</span></div></div><div><div><div>86</div></div><div>
</div></div><div><div><div>87</div></div><div><span>        </span><span>return</span><span> () </span><span>=&gt;</span><span> (</span></div></div><div><div><div>88</div></div><div><span>          </span><span>&lt;</span><span>ElDialog</span><span> </span><span>modelValue</span><span>=</span><span>{visible.</span><span>value</span><span>} {</span><span>...</span><span>{ </span><span>...</span><span><span>defaultDialogProps</span><span>, </span></span><span>...</span><span><span>dialogOptions</span><span> }} </span></span><span>onClosed</span><span>=</span><span>{</span><span>destroy</span><span>}</span><span>&gt;</span></div></div><div><div><div>89</div></div><div><span><span>            </span></span><span>{</span><span>CustomComponent</span><span>}</span></div></div><div><div><div>90</div></div><div><span>          </span><span>&lt;/</span><span>ElDialog</span><span>&gt;</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><span>    </span><span>isProvideAppContext</span><span> </span><span>&amp;&amp;</span><span><span> (</span><span>vnode</span><span>.</span></span><span>appContext</span><span> </span><span>=</span><span><span> </span><span>mainApp</span><span>.</span></span><span>_context</span><span>);</span></div></div><div><div><div>95</div></div><div><span>    </span><span>try</span><span> {</span></div></div><div><div><div>96</div></div><div><span>      </span><span>render</span><span>(</span><span>vnode</span><span>, </span><span>container</span><span>);</span></div></div><div><div><div>97</div></div><div><span><span>    </span></span><span>} </span><span>catch</span><span> (</span><span>err</span><span>) {</span></div></div><div><div><div>98</div></div><div><span>      </span><span>reject</span><span>(</span><span>err</span><span>);</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span><span>    </span></span><span>document</span><span>.</span><span>body</span><span>.</span><span>appendChild</span><span>(</span><span>container</span><span>);</span></div></div><div><div><div>102</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>103</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>首先，我们在外部没有声明控制弹窗显隐的状态，那么应该如何控制弹窗的显隐呢？答案是将控制的方法注入到子组件内，我们使用vue的api<code>provide</code>和<code>inject</code>来实现，那么这个问题就很好的被解决了</p></section><section><h3>上下文<a href="#上下文"><span>#</span></a></h3><p>你可以发现我们是使用<code>render</code>方法来挂载的组件,这会有一个严重的问题,那就是意味着,在我们的组件内显示的所有内容都脱离主应用的上下文,比如我们无法接收到来自主应用顶部注入的的变量,无法访问状态管理以及路由等信息.我中间尝试过将主应用的上下文复制过来给到组件,但是发现是不可行的.
这个时候,我又想起了messageBox,它是怎么解决的呢?我发现它是直接把主应用的上下文拿过来直接给到了组件的上下文.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>isProvideAppContext</span><span> </span></span><span>&amp;&amp;</span><span><span> (</span><span>vnode</span><span>.</span></span><span>appContext</span><span> </span><span>=</span><span><span> </span><span>mainApp</span><span>.</span></span><span>_context</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>直接引用了上下文,我发现组件内的内容也能拿到上下文了,甚好.</p><p>但是新的问题出现了,在经过这一步之后热更新会报错,由于错误是直接被热更新插件消费了,只是用console.error打印出来了.这也导致我们无法捕获这个错误,那么没办法,我只能重写error方法了.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 判断环境,这里我用的是vite</span></div></div><div><div><div>2</div></div><div><span>if</span><span> (</span><span>import</span><span>.</span><span>meta</span><span>.</span><span>env</span><span>.</span><span>DEV</span><span>) {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>errorLog</span><span> </span><span>=</span><span><span> </span><span>console</span><span>.</span></span><span>error</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>error</span><span> </span><span>=</span><span> (</span><span>...</span><span><span>args</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span><span> (</span><span>args</span><span>[</span></span><span>0</span><span>].</span><span>toString</span><span>().</span><span>includes</span><span>(</span><span>`TypeError: Cannot read properties of null (reading 'nextSibling')`</span><span>)) {</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>window</span><span>.</span><span>location</span><span>.</span><span>reload</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>errorLog</span><span>(</span><span>...</span><span><span>args</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></code></pre><div><div></div><div></div></div></figure></div><p>这样就成功解决了.</p></section><section><h3>支持promise<a href="#支持promise"><span>#</span></a></h3><p>这个很简单,只需要我们调用的方法返回一个promise就好了,然后将resolve,reject都给到组件内的子孙组件,由他们来决定执行的时机</p></section><section><h3>TODO 实现嵌套弹窗<a href="#todo-实现嵌套弹窗"><span>#</span></a></h3><p>这个暂时项目里还没有用到,等用到了再实现吧,大致思路是维护一个栈结构来实现</p></section></section>
<section><h2>命令式带来的好处<a href="#命令式带来的好处"><span>#</span></a></h2><p>如果你的vue3应用配置了jsx支持,那么下边的是两个用例,代码就变成了下边的样子,变得如此简洁!开发弹窗也可以很舒服了</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>add</span><span> </span><span>=</span><span> </span><span>async</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>await</span><span> </span><span>CommandDialog</span><span>(&lt;</span><span>SysDictAddEdit</span><span> /&gt;, {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>title</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></span><span>} </span><span>finally</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>getList</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>const</span><span> </span><span>edit</span><span> </span><span>=</span><span> </span><span>async</span><span><span> (</span><span>row</span></span><span>:</span><span> </span><span>IDictionaryResponseVo</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>await</span><span> </span><span>CommandDialog</span><span>(&lt;</span><span>SysDictAddEdit</span><span> </span><span>raw</span><span><span>={</span><span>row</span><span>} /&gt;, {</span></span></div></div><div><div><div>14</div></div><div><span>      </span><span>title</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><span>finally</span><span> {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>getList</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></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果出现报错,这里的报错等信息在内容组件内就被消费了,所以外部看起如此简洁,当然如果你希望抛出来处理也是没啥问题的</p><p>=.=,除非让我遇到不适用的场景,不然再也回不去声明式了.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/ji-lu-yi-ci-xian-shang-wen-ti-pai-cha/</id>
      <title type="text">记录一次线上问题排查</title>
      <published>2023-03-08T17:41:30.000Z</published>
      <updated>2023-03-08T17:41:30.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/ji-lu-yi-ci-xian-shang-wen-ti-pai-cha/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>这是一个什么样问题?<a href="#这是一个什么样问题"><span>#</span></a></h2><p>首先说明一下场景:
项目是一个采用vue3构建的应用,一直工作的好好的,但是今天测试那边突然说有几个页面打不开了,只要一点进去浏览器就会卡死.然后我们前端就在本地查看了下那几个页面,但是奇怪的是本地能运行的好好的,一到线上就拉闸,而且浏览器没有任何报错,这就很复杂了,这类本地无法复现的bug是最伤脑经的,因为某些原因几乎无法本地调试(以后要想个办法).</p></section>
<section><h2>初步排查<a href="#初步排查"><span>#</span></a></h2><p>我们检查了一下那几个特定的页面,初步的试验了一下我们的想法,但最后都排除了这些可能.我们还想到昨天以及前些日子不是还好好的吗?我们就查看了下git记录,发现那些改动几乎不会影响到流程,都是些文字改动,但是为了确保真的不是这个引起的,我们还是尝试了回退,结果也的确如我们猜想,不是这些改动造成的.</p></section>
<section><h2>好吧,那就只有慢慢排除了<a href="#好吧那就只有慢慢排除了"><span>#</span></a></h2><p>我们首先尝试了一下,将那几个页面替换为毫无逻辑,且模板简单的结构,打包部署上去发现还真没问题,好,看起来有路子.然后我们就一步一步的排查起走了,慢慢将页面还原,认为这样就能定位到问题所在了,几乎尝试了所有的可能,页面已经完全还原的情况下还是无法排查出问题.我靠,这是为啥?!百思不得其解啊.</p></section>
<section><h2>转机<a href="#转机"><span>#</span></a></h2><p>就在我们无限霸屏Jenkins的几个小时后,同事一个想法冒了出来,我们要不要本地打个包让运维手动放到服务器上试试?”环境问题?""我想是的,试试看”.这一试,还真就是这个问题!然后我们思考了为什么会是这样,于是就发现项目里的*.lock的文件并没有被git追踪记录,那么服务器那边clone代码后,也是没有这个文件的,这就会造成一个问题是,包的版本是动态的,虽然我们使用”^“来锁定大版本,但是小版本之间的差异依然是存在的,可能就是这个原因造成的.那行动吧,我们让git追踪了这些lock文件,然后将它上传到了仓库.Jenkins部署后问题也不存在了.哇~感觉浪费了好多时间哦,要是一开始能想到这个问题多好,搞笑的是,我以前的项目明明遇到过这个问题,但是我居然没有想到会是这个问题.</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>编程的问题,大致是这样,如果一个问题你在你的范围内排查了很久,但是依然没有结果,你可能就需要跳出你的圈子来思考外部因素,换一种思路去思考问题.要结合你以往的经验来大胆猜想,并合理假设,然后加以验证来确认问题的所在.试想,如果没有同事这一句话,可能到下班的点,我们还在排查是不是代码问题.想想都觉得刺激~</p><p>但其实还有个问题是,我们发现这几个页面和其他页面一样,也没有什么特别之处,没有用到什么新的特性啊,奇怪的写法等等,但是就是有问题,但这个其实并不值得去深究,在前端工程化之后,每个工具链都有可能更新后出现问题(虽然这种几率在一些控制下很小),如果需要去排查这些构建工具上边的问题,那可是个体力活.我们今天知道了大致的原因其实就算成功了.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/worker-xian-cheng/</id>
      <title type="text">Worker线程</title>
      <published>2023-02-20T12:51:08.000Z</published>
      <updated>2023-02-20T12:51:08.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/worker-xian-cheng/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>Worker线程简介<a href="#worker线程简介"><span>#</span></a></h2><p>js在我们的认知中一直是一门纯粹的单线程语言，但是worker线程的出现，让它的单线程特性变得不那么纯粹了~
worker线程是独立于主线程的一个线程，所以即使它遇到大量密集的计算，主线程也不会受影响。这也是我们使用它的一个原因，我们可以把比较繁重的任务交给它来执行，我们只需要等待最后的结果即可。
但是开启一个worker线程，代价也是比较大的（其实几乎任何语言，开启线程代价都是需要考量的，不频繁的创建销毁线程，会有线程池这些东西的存在都是有原因的），因为worker线程有着自己的独立的运行环境，这意味着一个js环境里有的东西，在它的环境里也需要基本具备，可想而知是这是多么巨大的代价。所以，一般我们需要长期运行的任务交给worker线程才是最合适的。</p></section>
<section><h2>Worker线程的分类<a href="#worker线程的分类"><span>#</span></a></h2><ul>
<li>
<p>专用工作者线程 这可能是我们以后使用最多的一个类型，它可以让脚本单独创建一个JavaScript线程，用来执行我们的任务，专用的意思是他只能被创建它的页面使用</p>
</li>
<li>
<p>共享工作者线程 与专用工作者线程类似，但是它不‘专用’，它可以被不同的上下文，页面等使用</p>
</li>
<li>
<p>服务工作者线程 主要用途是拦截，重定向和修改页面发出的请求</p>
</li>
</ul></section>
<section><h2>WorkerGlobalScope<a href="#workerglobalscope"><span>#</span></a></h2><p>在浏览器环境中，我们的全局对象通常是window，但是在工作者线程中是没有window对象的，它的全局对象就是<code>WorkerGlobalScope</code>,在脚本中通过self关键字暴露出来，其上会有一些和工作者线程有关的成员，它可以看作是window的子集，当然不说你也应该直到，DOM的API在它上边肯定是没有的</p><p>除了拥有window上的一些成员外，他还有一个特有的方法<code>importScripts</code>，用来动态的开启另外一个线程</p></section>
<section><h2>专用工作者线程<a href="#专用工作者线程"><span>#</span></a></h2><p>它可以用来解决在遭遇大量计算时页面的慢响应问题。它可以与主线程交换信息，发送网络请求，执行文件输入和输出，大量的密集的计算任务等</p><p>先来一个简单使用示例</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>worker</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker.js"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们在主页使用Worker构造器，导入了一个worker.js，即使这个文件是空白的，我们也已经成功的开启了一个工作者线程．注意，脚本文件只能从同源加载，否则会报错，但是使用<code>importScripts</code>加载脚本可以不用受同源的限制，因为这个时候已经脱离了父文档的上下文了。</p><p>然后我们使用了一个worker变量去接受工作者线程的代理对象，以后交流通信可都得靠它啦~</p><section><h3>通信<a href="#通信"><span>#</span></a></h3><p>通信我们可以使用代理对象上的事件来进行通信，下边是一个简单的示例</p><p>我们在worker.js中写入下面的代码</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>self</span><span>.</span></span><span>addEventListener</span><span>(</span><span>'message'</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>var</span><span><span> </span><span>data</span><span> </span></span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>data</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>, </span><span>data</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>self</span><span>.</span><span>postMessage</span><span>(</span><span>'hello,主线程'</span><span>);</span></div></div><div><div><div>5</div></div><div><span>})</span></div></div><div><div><div>6</div></div><div><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>self</span><span>.</span><span>postMessage</span><span>(</span><span>'hello,主线程'</span><span>);</span></div></div><div><div><div>8</div></div><div><span>}, </span><span>1000</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>// 创建一个worker线程</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>worker</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker.js"</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>worker</span><span>.</span></span><span>addEventListener</span><span>(</span><span>"message"</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>console</span><span>.</span><span>log</span><span>(</span><span>"从worker线程收到的数据"</span><span><span>, </span><span>e</span><span>.</span></span><span>data</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>worker</span><span>.</span></span><span>postMessage</span><span>(</span><span>"hello worker"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样,我们就实现了基本的通信了,我们采用事件的方式完成了通信.</p><p>worker代理对象和工作者线程全局对象上都支持下面的的事件:</p><ul>
<li>onerror 发生错误时</li>
<li>onmessage 收到消息时</li>
<li>onmessageerror MessageEvent</li>
</ul><p>它们还有下面的方法:</p><ul>
<li>postMessage 异步发送消息</li>
<li>terminate 代理对象调用,立即终止线程,几乎不会顾及任何后果,直接停止</li>
<li>close 在工作者脚本内调用,立即终止线程,几乎不会顾及任何后果,直接停止</li>
</ul><p>专用工作者线程的全局对象是 <code>DedicatedWorkerGlobalScope</code>,他继承自<code>WorkerGlobalScope</code>,他还有独有一个name属性,是Worker构造器可选的标识符参数,可以理解为用来给线程命名,说到这里了,顺便来看下Worker构造器可选的配置项吧</p><ul>
<li>name 线程标识符</li>
<li>type 运行方式 classic | module</li>
<li>credentials type为moudle时可用</li>
</ul></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>worker2</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span></div></div><div><div><div>2</div></div><div><span>  </span><span>URL</span><span>.</span><span>createObjectURL</span><span>(</span></div></div><div><div><div>3</div></div><div><span>    </span><span>new</span><span> </span><span>Blob</span><span>([</span><span>"console.log('hello worker2')"</span><span>], {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>"application/javascript"</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></div></div><div><div><div>7</div></div><div><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们使用了字符串创建了Blob,然后又通过Blob创建了URL,把URL给到了Worler构造器,完成啦</p><p>但是注意这个字符串代表的函数不能引用外部的变量或者通过闭包引用的变量,因为在工作者线程中,这些引用可能不复存在,那么你的程序就会报错</p></section><section><h3>importScripts<a href="#importscripts"><span>#</span></a></h3><p>我们可以使用<code>importScripts</code>这个api在工作者线程中动态的引入脚本,而且这些脚本源是可以不受同源策略的限制的,你甚至可以在新的脚本中开启新的工作者线程,这也是不受同源策略影响的,因为在工作者线程中已经脱离了腹肌上下文,你终于可以放开手脚去干了~</p><p>以下是一个使用实例,我们在worker.js中写入以下代码即可</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>importScripts</span><span>(</span><span>'./A.js'</span><span>)</span></div></div><div><div><div>2</div></div><div><span>importScripts</span><span>(</span><span>'./B.js'</span><span>)</span></div></div><div><div><div>3</div></div><div><span>importScripts</span><span>(</span><span>'./C.js'</span><span>,</span><span>'./D.js'</span><span>,)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你可以看到这个api可以一次引入多个脚本,他们会按照你书写的顺序来执行</p></section><section><h3>关于错误捕获<a href="#关于错误捕获"><span>#</span></a></h3><p>在工作者线程中如果发生错误,是不会冒泡到主线程的,这样设计是担心其影响主线程的执行,虽然你在主线程无法捕获错误，但是在工作者线程内部错误可以像往常一样被捕获</p><p>主线程，❌</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>worker</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker.js"</span><span>);</span></div></div><div><div><div>2</div></div><div><span>try</span><span>{</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>worker</span><span>.</span><span>addEventListener</span><span>(</span><span>"message"</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>console</span><span>.</span><span>log</span><span>(</span><span>"从worker线程收到的数据"</span><span><span>, </span><span>e</span><span>.</span></span><span>data</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>worker</span><span>.</span><span>postMessage</span><span>(</span><span>"hello worker"</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span><span>catch</span><span><span>(</span><span>err</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>try</span><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'出现异常'</span><span>)</span></div></div><div><div><div>3</div></div><div><span>}</span><span>catch</span><span><span>(</span><span>err</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>err</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>const</span><span> </span><span>worker</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker.js"</span><span>);</span></div></div><div><div><div>2</div></div><div><span><span>worker</span><span>.</span></span><span>addEventListener</span><span>(</span><span>"error"</span><span><span>, (</span><span>e</span><span>) </span></span><span>=&gt;</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>e</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>工作者线程之间的通信<a href="#工作者线程之间的通信"><span>#</span></a></h3><p>我们已经完成了主线程与工作者线程之间的通信,那么如何完成工作者线程之间的通信呢？</p><p>你可能会想到一种方式，那就是主线程来负责这件事情，在主线程收到来自工作者线程的信息，将其转发给对应的工作者线程，但是这样的问题是主线程会多了很多无关的逻辑代码，其实这些都是主线程没必要去关心的</p><p>为了解决这个问题，我们可以使用<code>MessageChannel</code>实例来让工作者线程之间建立一个通信道，之后他们通信会在这个信道进行，主线程是不关心的，甚至是无感的。下边是一个简单的示例：</p><p>主线程：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>worker</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker1.js"</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>worker2</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker2.js"</span><span>);</span></div></div><div><div><div>3</div></div><div><span>const</span><span> { </span><span>port1</span><span>, </span><span>port2</span><span> } </span><span>=</span><span> </span><span>new</span><span> </span><span>MessageChannel</span><span>();</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 将信道的两个端口分别发送给两个 worker</span></div></div><div><div><div>6</div></div><div><span><span>worker</span><span>.</span></span><span>postMessage</span><span>({ </span><span>port</span><span>:</span><span><span> </span><span>port1</span><span> }, [</span><span>port1</span><span>]);</span></span></div></div><div><div><div>7</div></div><div><span><span>worker2</span><span>.</span></span><span>postMessage</span><span>({ </span><span>port</span><span>:</span><span><span> </span><span>port2</span><span> }, [</span><span>port2</span><span>]);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以看到我们创建了一个<code>MessageChannel</code>实例，并拿到它的双端，然后把端口分别给到了两个工作者线程，接下来的事情就是两个工作者线程使用端口进行通信</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// worker1</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>port</span></span></div></div><div><div><div>3</div></div><div><span><span>self</span><span>.</span></span><span>onmessage</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>e</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>e</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>port</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>port</span><span> </span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>data</span><span>.</span><span>port</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>self</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span> </span><span>null</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>port</span><span>.</span><span>postMessage</span><span>(</span><span>'hello world'</span><span>)</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>port</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>e</span><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><span>e</span><span>.</span></span><span>data</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><span>}</span></div></div><div><div><div>14</div></div><div><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>port</span><span>.</span><span>postMessage</span><span>(</span><span>'hello worker2,我是worker1'</span><span>)</span></div></div><div><div><div>16</div></div><div><span>}, </span><span>1000</span><span>);</span></div></div><div><div><div>17</div></div><div><span>// worker2</span></div></div><div><div><div>18</div></div><div><span>let</span><span><span> </span><span>port</span></span></div></div><div><div><div>19</div></div><div><span><span>self</span><span>.</span></span><span>onmessage</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>port</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>port</span><span> </span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>data</span><span>.</span><span>port</span><span>;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>self</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span> </span><span>null</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>port</span><span>.</span><span>postMessage</span><span>(</span><span>'hello world'</span><span>)</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>port</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>25</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>data</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></div></div><div><div><div>29</div></div><div><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>port</span><span>.</span><span>postMessage</span><span>(</span><span>'hello worker1,我是worker2'</span><span>)</span></div></div><div><div><div>31</div></div><div><span>}, </span><span>1000</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>转移</code>的概念</p><p>另外使用<code>MessageChannel</code>实例来进行主线程和工作者线程之间的通信时可行的，但是这样做时没有必要的，因为这与直接调用<code>postmessage</code>没有任何差异，没必要新建一个消息信道</p><p>这里再说以下另外一种通信，<code>BroadcastChannel</code>,他的行为更直接，没有端口所有权的概念，更像是一个订阅发布中心。他的使用方式可以去了解一下：<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/BroadcastChannel" target="_blank">MDN-BroadcastChannel</a></p></section><section><h3>数据传递<a href="#数据传递"><span>#</span></a></h3><p>在前边我们传递的数据都是字符串，而且看起来工作正常，这是因为序列化字符串是会被直接复制交给目标上下文的，但是在遇到一些复杂的数据类型，情况就不是那么正常了。</p><p>在工作者线程传输数据中，有三种转移信息的方式:</p><ul>
<li>可转移对象 可转移对象是指将该对象转交给目标上下文，当然转交后自身线程就不再拥有该对象的控制权了，必须等到这个对象再次被转移回来才能再次拥有控制权；将对象转移的方式是postmessage的第二个参数，他是一个数组，包裹着即将转移的对象。即使你的转移对象嵌套在其他数据类型中也没有关系，嵌套的可转移对象还是会被转移，而嵌套的数据会被结构化克隆
<ul>
<li>ArrayBuffer</li>
<li>MessagePort</li>
<li>ImageBitmap</li>
<li>OffscreenCanvas 一个非常有意思的可转移对象，这也是我们日常口中所说的canvas</li>
</ul>
</li>
<li>结构化克隆算法 该方式会克隆一个副本，除了DOM节点以及error，function之外几乎都可以克隆，使用前可以先进行测试下该数据是否可以被克隆，但是红宝书中有提到在某些情况下可能会得到并不是完全一致的数据，注意原型链不会被克隆</li>
<li>共享缓冲区 这种方式既不克隆，也不会转移，可以在所有上下文中使用，SharedArrayBuffer在传递的过程中始终是引用传递。这种方式虽然简单粗暴，但是这会导致一个严重的线程安全问题。这就需要我们引入线程锁的概念！</li>
</ul><p>这里我们重点演示共享缓冲区，因为其余两个你只需要知道他们的工作方式即可，但是共享缓冲区的线程安全问题是不容忽视的。</p><p>main.js</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>works</span><span> </span><span>=</span><span> </span><span>Array</span><span>(</span><span>4</span><span>)</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>.</span><span>fill</span><span>(</span><span>0</span><span>)</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>.</span><span>map</span><span>(() </span><span>=&gt;</span><span> </span><span>new</span><span> </span><span>Worker</span><span>(</span><span>"./worker.js"</span><span>));</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>sabf</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>SharedArrayBuffer</span><span>(</span><span>4</span><span>);</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>view</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Uint32Array</span><span><span>(</span><span>sabf</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span><span>view</span><span>[</span></span><span>0</span><span>] </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>works</span><span>.</span></span><span>forEach</span><span><span>((</span><span>w</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>w</span><span>.</span></span><span>postMessage</span><span><span>(</span><span>sabf</span><span>));</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>worker.js</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>self</span><span>.</span></span><span>addEventListener</span><span>(</span><span>"message"</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>const</span><span> </span><span>view</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Uint32Array</span><span><span>(</span><span>data</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>// 执行 100 万次加操作</span></div></div><div><div><div>4</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>1e6</span><span>; </span><span>++</span><span><span>i</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 使用Atomics来解决线程安全问题</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>Atomics</span><span>.</span><span>add</span><span><span>(</span><span>view</span><span>, </span></span><span>0</span><span>, </span><span>1</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>如果你有兴趣打印出view的值,你会发现是如期所示的40001,这段代码都摘抄自红宝书,可以自行验证(注意SharedArrayBuffer,由于当年的幽灵漏洞等问题,被禁止过一段时间,如果你现在想使用,则必须遵守同源策略,或者设置跨域响应头)</p></section><section><h3>关于线程池<a href="#关于线程池"><span>#</span></a></h3><p>为了避免频繁的创建销毁线程,一般我们会采用线程池来管理我们的线程,这一段在红宝书里有着详细的代码,依据封装行为的不同,代码的表现形式可能不太一样.但是基本思路如下:</p><ul>
<li>我们会拓展我们的线程对象,让其有一个空闲和繁忙的两个状态</li>
<li>我们还会有一个调度对象,用来管理线程池中的线程,以及维护一个待执行的任务队列</li>
</ul><p>下边直接贴红宝书的代码,红宝书这里采用了类似于promise的风格来表示线程的状态</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>TaskWorker</span><span> </span><span>extends</span><span> </span><span>Worker</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>notifyAvailable</span><span>, </span></span><span>...</span><span><span>workerArgs</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>super</span><span>(</span><span>...</span><span><span>workerArgs</span><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>this</span><span>.</span><span>available</span><span> </span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>resolve</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>reject</span><span> </span><span>=</span><span> </span><span>null</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>// 以便工作者线程发出它需要新任务的信号</span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>notifyAvailable</span><span> </span><span>=</span><span><span> </span><span>notifyAvailable</span><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>// 会发送一条"ready"消息</span></div></div><div><div><div>13</div></div><div><span>    </span><span>this</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>setAvailable</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></div></div><div><div><div>16</div></div><div><span>  </span><span>dispatch</span><span><span>({ </span><span>resolve</span><span>, </span><span>reject</span><span>, </span><span>postMessageArgs</span><span> }) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>this</span><span>.</span><span>available</span><span> </span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>this</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span><span> ({ </span><span>data</span><span> }) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>resolve</span><span><span>(</span><span>data</span><span>);</span></span></div></div><div><div><div>20</div></div><div><span>      </span><span>this</span><span>.</span><span>setAvailable</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>this</span><span>.</span><span>onerror</span><span> </span><span>=</span><span><span> (</span><span>e</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>23</div></div><div><span>      </span><span>reject</span><span><span>(</span><span>e</span><span>);</span></span></div></div><div><div><div>24</div></div><div><span>      </span><span>this</span><span>.</span><span>setAvailable</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>this</span><span>.</span><span>postMessage</span><span>(</span><span>...</span><span><span>postMessageArgs</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>setAvailable</span><span>() {</span></div></div><div><div><div>29</div></div><div><span>    </span><span>this</span><span>.</span><span>available</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>resolve</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>31</div></div><div><span>    </span><span>this</span><span>.</span><span>reject</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>32</div></div><div><span>    </span><span>this</span><span>.</span><span>notifyAvailable</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>class</span><span> </span><span>WorkerPool</span><span> {</span></div></div><div><div><div>37</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>poolSize</span><span>, </span></span><span>...</span><span><span>workerArgs</span><span>) {</span></span></div></div><div><div><div>38</div></div><div><span>    </span><span>this</span><span>.</span><span>taskQueue</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>39</div></div><div><span>    </span><span>this</span><span>.</span><span>workers</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>40</div></div><div><span>    </span><span>// 初始化线程池</span></div></div><div><div><div>41</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>poolSize</span><span>; </span></span><span>++</span><span><span>i</span><span>) {</span></span></div></div><div><div><div>42</div></div><div><span>      </span><span>this</span><span>.</span><span>workers</span><span>.</span><span>push</span><span>(</span></div></div><div><div><div>43</div></div><div><span>        </span><span>new</span><span> </span><span>TaskWorker</span><span>(() </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>dispatchIfAvailable</span><span>(), </span><span>...</span><span><span>workerArgs</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></span><span>}</span></div></div><div><div><div>47</div></div><div><span>  </span><span>// 把任务推入队列</span></div></div><div><div><div>48</div></div><div><span>  </span><span>enqueue</span><span>(</span><span>...</span><span><span>postMessageArgs</span><span>) {</span></span></div></div><div><div><div>49</div></div><div><span>    </span><span>return</span><span> </span><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>50</div></div><div><span>      </span><span>this</span><span>.</span><span>taskQueue</span><span>.</span><span>push</span><span><span>({ </span><span>resolve</span><span>, </span><span>reject</span><span>, </span><span>postMessageArgs</span><span> });</span></span></div></div><div><div><div>51</div></div><div><span>      </span><span>this</span><span>.</span><span>dispatchIfAvailable</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><span>// 把任务发送给下一个空闲的线程（如果有的话）</span></div></div><div><div><div>55</div></div><div><span>  </span><span>dispatchIfAvailable</span><span>() {</span></div></div><div><div><div>56</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>this</span><span>.</span><span>taskQueue</span><span>.</span><span>length</span><span>) {</span></div></div><div><div><div>57</div></div><div><span>      </span><span>return</span><span>;</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>for</span><span> (</span><span>const</span><span> </span><span>worker</span><span> </span><span>of</span><span> </span><span>this</span><span>.</span><span>workers</span><span>) {</span></div></div><div><div><div>60</div></div><div><span>      </span><span>if</span><span><span> (</span><span>worker</span><span>.</span></span><span>available</span><span>) {</span></div></div><div><div><div>61</div></div><div><span>        </span><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>this</span><span>.</span><span>taskQueue</span><span>.</span><span>shift</span><span>();</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>worker</span><span>.</span><span>dispatch</span><span><span>(</span><span>a</span><span>);</span></span></div></div><div><div><div>63</div></div><div><span>        </span><span>break</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>}</span></div></div><div><div><div>67</div></div><div><span>  </span><span>// 终止所有工作者线程</span></div></div><div><div><div>68</div></div><div><span>  </span><span>close</span><span>() {</span></div></div><div><div><div>69</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> </span><span>worker</span><span> </span><span>of</span><span> </span><span>this</span><span>.</span><span>workers</span><span>) {</span></div></div><div><div><div>70</div></div><div><span><span>      </span></span><span>worker</span><span>.</span><span>terminate</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>  </span></span><span>}</span></div></div><div><div><div>73</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>另外在线程池中的线程数量多少为最佳的问题,我们一般直接采用<code>navigator.hardware Concurrency</code>返回的核心数量来决定,当然这些都不是绝对的哈</p><p>至此,你其实已经可以开心的当一个多线程js boy/girl了.接下来要说的共享工作者线程,以及服务工作者线程你可以选择性的进行观看.</p></section></section>
<section><h2>共享工作者线程<a href="#共享工作者线程"><span>#</span></a></h2><p>共享工作者线程大体上和专用工作者线程是相似的,但是共享工作者线程是可以被同源的不同上下文,页面,标签,内嵌框架(ifram)使用的,而且会根据URL来创建唯一的共享工作者线程,这意味着你使用同样的URL第二次创建的共享工作者线程是同一个,第二次创建只是帮你链接到了第一次创建的线程.这里值得一说的是,即使你使用了不同的字符串路径来创建线程,如果最终定位到的文件是同一个,依然会遵循唯一的原则,只会创建一次.共享工作者线程是否新创建线程取决于线程标识符是否唯一,线程标识符=URL+可选参name.比如下方,即使URL一致,还是会开启两个共享工作者线程.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>w1</span><span>=</span><span>new</span><span> </span><span>Worker</span><span>(</span><span>'./sworker.js'</span><span>,{</span><span>name</span><span>:</span><span>'a'</span><span>})</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>w2</span><span>=</span><span>new</span><span> </span><span>Worker</span><span>(</span><span>'./sworker.js'</span><span>,{</span><span>name</span><span>:</span><span>'b'</span><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>他的使用方式其实也和专用工作者线程有着一些细微的区别,不管是第一次创建还是被连接到该线程,都会在线程内部触发<code>connect</code>事件,这会隐式的创建MessageChannel实例对象,并将端口放置在<code>connect</code>事件对象的<code>ports</code>数组中,你可以使用<code>ports[0]</code>来将该端口保存下来,一个端口代表一个被链接进来的通信链接.</p><p>注意由于共享工作者线程工作的特性,可能不会因为你这个页面关闭而销毁,所以会导致内部端口越来越多,如果长期没有关闭浏览器,就会导致连接事件保存起来的端口越来越多,会造成严重的端口污染.推荐做法是某个时刻,比如关闭页面的时候通知线程清理对应的端口.</p></section>
<section><h2>服务工作者线程<a href="#服务工作者线程"><span>#</span></a></h2><p>服务工作者线程是一种类似于代理的线程,可以拦截请求和缓存响应.可以让网页在断网的情况下依然可用,可以像一个原生的App一样,这就是因为他的缓存功效.</p><p>这里不去做更多的关于服务工作者线程的阐述,因为他可以看作是一种应用增强的技术,然我们的应用具备更多的原生特性.不过如果你有兴趣,可以尝试去研究下他的请求拦截等方式,还是比较有意义的.</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>我相信,在工作者线程落地的那一瞬间,整个js社区是兴奋的,因为我们js也有了多线程能力!虽然不如其他多线程语言那般强大,但是我们也已经很知足了!像Egg.js等框架就已经实装了工作者线程.</p><p>最后,希望在以后的工作中有机会就多实践一下工作者线程,来切切实实的感受到它的魅力所在吧!</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/bian-li-dom-jie-dian-de-fang-shi-you-zeng-jia-ge/</id>
      <title type="text">遍历DOM节点的方式又增加咯~</title>
      <published>2023-02-11T14:01:38.000Z</published>
      <updated>2023-02-11T14:01:38.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/bian-li-dom-jie-dian-de-fang-shi-you-zeng-jia-ge/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>前情提要<a href="#前情提要"><span>#</span></a></h2><p>假设我们现在有以下DOM结构，我们现在的需求是要去遍历li标签做一些操作，但是有一个额外条件，我们不需要遍历ol标签下的li标签</p><p>环境说明：我们所有的代码会包裹在一个异步函数内来作为顶级async支持，并提供了一个thenable接口对象，他会模拟代码暂停一秒钟，避免遍历过快，方便观察</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;我害怕你心碎没人帮你擦眼泪&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;别管那是非 只要我们感觉对&lt;/</span><span>li</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;我害怕你心碎没人帮你擦眼泪&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;别离开身边 拥有你我的世界才能完美&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;</span><span>ol</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;哎呦 吉他谁教你的&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;哎呦 我生下来就会啊 你不知道啊&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;哎呦 屁咧&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;/</span><span>ol</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><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>li</span><span>&gt;才离开没多久就开始&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;担心今天的你过得好不好&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>&lt;</span><span>li</span><span>&gt;整个画面是你&lt;/</span><span>li</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;想你想的睡不着&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span>&lt;/</span><span>ul</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><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>lis</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelectorAll</span><span>(</span><span>"li"</span><span>);</span></div></div><div><div><div>2</div></div><div><span>// OR</span></div></div><div><div><div>3</div></div><div><span>// const lis = document.querySelectorAll("ul li");</span></div></div><div><div><div>4</div></div><div><span>for</span><span> (</span><span>const</span><span> </span><span>l</span><span> </span><span>of</span><span><span> </span><span>lis</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>  </span><span>if</span><span><span> (</span><span>l</span><span>.</span></span><span>parentNode</span><span>.</span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"OL"</span><span>) </span><span>continue</span><span>;</span></div></div><div><div><div>6</div></div><div><span>  </span><span>await</span><span><span> </span><span>sleepThenable</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><span>l</span><span>.</span></span><span>innerHTML</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><p>我们其实也可以使用<code>NodeIterator</code>和<code>TreeWalker</code>类型来解决这个问题。他们都是DOM2定义的辅助我们来遍历DOM的类型，遍历dom的方式是使用迭代器模式。注意，这两个类型都是深度优先遍历。</p></section>
<section><h2>使用NodeIterator的方式<a href="#使用nodeiterator的方式"><span>#</span></a></h2><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>const</span><span> </span><span>wts</span><span> </span><span>=</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>SHOW_ELEMENT</span><span>;</span></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>nodeFilter</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>acceptNode</span><span><span>(</span><span>node</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"LI"</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>      </span><span>return</span><span><span> </span><span>node</span><span>.</span></span><span>parentNode</span><span>.</span><span>nodeName</span><span> </span><span>!==</span><span> </span><span>"OL"</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_ACCEPT</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>NodeFilter</span><span>.</span></span><span>FILTER_SKIP</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>const</span><span> </span><span>nodeIterator</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createNodeIterator</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></span><span>document</span><span>.</span><span>body</span><span>,</span></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></span><span>wts</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></span><span>nodeFilter</span></div></div><div><div><div>20</div></div><div><span>);</span></div></div><div><div><div>21</div></div><div><span>let</span><span><span> </span><span>node</span><span>;</span></span></div></div><div><div><div>22</div></div><div><span>while</span><span><span> ((</span><span>node</span><span> </span></span><span>=</span><span><span> </span><span>nodeIterator</span><span>.</span></span><span>nextNode</span><span>())) {</span></div></div><div><div><div>23</div></div><div><span>  </span><span>await</span><span><span> </span><span>sleepThenable</span><span>;</span></span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>node</span><span>.</span></span><span>innerHTML</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>你会发现得到和我们使用常规方式遍历DOM一样的结果。
说下使用流程吧：</p><ul>
<li>我们使用document.createNodeIterator创建了NodeIterator实例</li>
<li>在创建的时候接受了三个参数分别为&lt;根节点&gt;，遍历的节点类型，自定义过滤器，下边对后两个参数进行解释
<ul>
<li>节点类型是指我们需要遍历哪些节点类型，注意这里可以使用<code>|</code>运算符进行联合，进行多个节点类型的遍历</li>
<li>自定义过滤器可以看作是节点类型参数的一种补充，它是一个对象，他会有一个acceptNode函数作为属性，所以进行遍历的节点都会进行到这个函数，它如果返回<code>FILTER_SKIP</code>表示跳过这个节点，返回<code>FILTER_ACCEPT</code>表示接受这个节点的遍历</li>
</ul>
</li>
<li>然后我们使用了NodeIterator进行迭代器式遍历</li>
</ul><p>这种遍历方式将逻辑进行了抽离，更直观也更方便的进行维护。</p></section>
<section><h2>使用TreeWalker的方式<a href="#使用treewalker的方式"><span>#</span></a></h2><p>TreeWalker是对NodeIterator的进一步补充。使用方式基本没有差异，不过他有以下不同</p><ul>
<li>他的过滤器可以返回第三个值<code>FILTER_REJECT</code>,这表示这个节点以及他的子节点都将被忽略，如果你在NodeIterator里返回这个会等同于<code>FILTER_SKIP</code></li>
<li>他提供了我们遍历节点时进行跳跃的api,非常的好用</li>
</ul><p>看一下他的使用示例吧</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>nodeFilter2</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>acceptNode</span><span><span>(</span><span>node</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>parentNode</span><span>.</span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"OL"</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>return</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_REJECT</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"LI"</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>return</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_ACCEPT</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>return</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_SKIP</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>
</div></div><div><div><div>12</div></div><div><span>// TreeWalker</span></div></div><div><div><div>13</div></div><div><span>const</span><span> </span><span>treeWalker</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createTreeWalker</span><span>(</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>document</span><span>.</span><span>body</span><span>,</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>wts</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>nodeFilter2</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>while</span><span><span> ((</span><span>node</span><span> </span></span><span>=</span><span><span> </span><span>treeWalker</span><span>.</span></span><span>nextNode</span><span>())) {</span></div></div><div><div><div>20</div></div><div><span>  </span><span>await</span><span><span> </span><span>sleepThenable</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><span>node</span><span>.</span></span><span>innerHTML</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>这里我们改写了过滤器逻辑,使用它直接过滤了ol节点下所有内容。至于他的节点跳跃能力，我们下边来说</p></section>
<section><h2>需求来咯<a href="#需求来咯"><span>#</span></a></h2><p>我们有了一个新需求：当遍历到<code>担心今天的你过得好不好</code>,我们希望将焦点跳转到上一个ul列表的最后一句，也就是<code>别离开身边 拥有你我的世界才能完美</code>,然后继续遍历；接下来我们就来使用TreeWalker的跳跃能力来完成这个需求吧</p><p>我们先来介绍以下跳跃的api吧,知道了这些api我们就很好办事了~：</p><ul>
<li>parentNode() 遍历到当前节点的父节点。</li>
<li>firstChild() 遍历到当前节点的第一个子节点。</li>
<li>lastChild() 遍历到当前节点的最后一个子节点。</li>
<li>nextSibling() 遍历到当前节点的下一个同胞节点。</li>
<li>previousSibling() 遍历到当前节点的上一个同胞节点。</li>
</ul><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>nodeFilter2</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>acceptNode</span><span><span>(</span><span>node</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>parentNode</span><span>.</span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"OL"</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>      </span><span>return</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_REJECT</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"LI"</span><span> </span><span>||</span><span><span> </span><span>node</span><span>.</span></span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"UL"</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>return</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_ACCEPT</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>return</span><span><span> </span><span>NodeFilter</span><span>.</span></span><span>FILTER_SKIP</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>
</div></div><div><div><div>12</div></div><div><span>while</span><span><span> ((</span><span>node</span><span> </span></span><span>=</span><span><span> </span><span>treeWalker</span><span>.</span></span><span>nextNode</span><span>())) {</span></div></div><div><div><div>13</div></div><div><span>  </span><span>await</span><span><span> </span><span>sleepThenable</span><span>;</span></span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>node</span><span>.</span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"LI"</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>node</span><span>.</span></span><span>innerHTML</span><span>);</span></div></div><div><div><div>15</div></div><div><span>  </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>innerHTML</span><span> </span><span>===</span><span> </span><span>"担心今天的你过得好不好"</span><span>) {</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>treeWalker</span><span>.</span><span>parentNode</span><span>();</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>treeWalker</span><span>.</span><span>previousSibling</span><span>();</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>treeWalker</span><span>.</span><span>lastChild</span><span>();</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>treeWalker</span><span>.</span><span>previousSibling</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>由于我们需要在父节点间进行跳转，所以我们改写了过滤器的逻辑，将ul标签也放进了遍历列表</p><p>然后说一下这里迭代时发生了什么，我们在走到<code>担心今天的你过得好不好</code>这一句时，先重定向到了父级节点，然后跳到父级节点的上一个兄弟节点，然后跳到这个兄弟节点的最后一个节点，再跳到了这个节点的上一个兄弟节点，然后完成了这个回环</p><p>你或许会疑惑，这我用常规的循环也能完成啊，而且代码量更少，然后贴出了这样的代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</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>lis</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>if</span><span><span> (</span><span>lis</span><span>[</span><span>i</span><span>].</span></span><span>parentNode</span><span>.</span><span>nodeName</span><span> </span><span>===</span><span> </span><span>"OL"</span><span>) </span><span>continue</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>await</span><span><span> </span><span>sleepThenable</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>lis</span><span>[</span><span>i</span><span>].</span></span><span>innerHTML</span><span>);</span></div></div><div><div><div>5</div></div><div><span>  </span><span>if</span><span><span> (</span><span>lis</span><span>[</span><span>i</span><span>].</span></span><span>innerHTML</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>i</span><span> </span><span>=</span><span> </span><span>2</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>但是这里的问题是跳转的索引是一个魔法代码，没有api调用式的语义化，变得难以理解；而且可能还会出现DOM结构一旦更改，这个数值就不准确咯，比如如果你数错了这个节点在整个列表的位置，那就G咯，这些东西会很大程度增大你的负担。</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>不错，好用，嘿嘿</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/ji-yu-axios-de-jie-kou-ji-fang-chong-fu-qing-qiu-feng-zhuang-an-li/</id>
      <title type="text">基于axios的接口级防重复请求封装案例</title>
      <published>2023-01-08T22:21:22.000Z</published>
      <updated>2023-01-08T22:21:22.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/ji-yu-axios-de-jie-kou-ji-fang-chong-fu-qing-qiu-feng-zhuang-an-li/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么需要防抖和节流来减少函数调用?<a href="#为什么需要防抖和节流来减少函数调用"><span>#</span></a></h2><p>在工作中,我们为了避免用户的误操作,以及程序的错误设置带来的过于频繁的执行某些操作,比如数据的增删改操作,比如用户的输入操作等,这些问题除了会影响应用性能问题外,甚至会导致程序出现一些错误.我们通常会使用节流和防抖来解决该类问题,我们通常的做法是使用节流和防抖来增强我们的函数让某些操作减少执行,所以我们今儿使用axios的取消操作来实现接口级防频繁请求.</p></section>
<section><h2>首先,我们需要了解axios取消请求的方式<a href="#首先我们需要了解axios取消请求的方式"><span>#</span></a></h2><p>我们首先来看一个axios的取消操作</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>cancelToken</span><span>;</span></span></div></div><div><div><div>2</div></div><div><span><span>axios</span><span>.</span></span><span>get</span><span>(</span><span>"xxx"</span><span>, {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>cancelToken</span><span>:</span><span> </span><span>new</span><span><span> </span><span>axios</span><span>.</span></span><span>CancelToken</span><span><span>((</span><span>cancel</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>cancelToken</span><span> </span><span>=</span><span><span> </span><span>cancel</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>// 我们只需要调用这个函数就可以直接取消这个请求</span></div></div><div><div><div>8</div></div><div><span>cancelToken</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>好的,前置知识已经完毕,接下来开始封装我们的axios吧</p></section>
<section><h2>开搞<a href="#开搞"><span>#</span></a></h2><p>首先创建一个axios实例,其配置参数可以自行去看下文档,这块大部分是根据自己项目以及业务来的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>request</span><span> </span><span>=</span><span><span> </span><span>axios</span><span>.</span></span><span>create</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>baseURL</span><span>:</span><span> </span><span>"xxx"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>timeout</span><span>:</span><span> </span><span>2000</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>然后是请求和响应的拦截器,我们也是在这两个钩子里大做文章,先来个雏形吧</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>request</span><span>.</span></span><span>interceptors</span><span>.</span><span>request</span><span>.</span><span>use</span><span>(</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>(</span><span>config</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span><span> </span><span>config</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><span>error</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span><span>(</span><span>error</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>error</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><span>request</span><span>.</span></span><span>interceptors</span><span>.</span><span>response</span><span>.</span><span>use</span><span>(</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>(</span><span>response</span><span>) </span><span>=&gt;</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>return</span><span><span> </span><span>response</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>err</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>// TODO 业务处理</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span><span>(</span><span>err</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></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>我们这里只是简单的将请求参数和响应正常返回,具体的逻辑应该根据自己的业务来,我们这次的封装只关心接口的防频繁处理.</p><p>接下来就是重点了,我们应该怎么来处理呢?而且要和axios本身进行很好的整合.不卖关子了,直接上逻辑.</p><p>主要逻辑是这样的:</p><ul>
<li>
<p>我们会维护一个Map对象,它主要用来保存该次请求所映射的axios取消函数</p>
</li>
<li>
<p>当新的请求进来的时候,我们在请求拦截器内要想办法去确定该次请求是不是重复请求</p>
<ul>
<li>A.如果是重复请求,那么我们就需要取消本次请求,直到上次的请求响应返回以后,新的请求才可以正常执行</li>
<li>B.如果不是重复请求,那就是正常的请求流程,将该次请求的映射关系放到Map对象内</li>
</ul>
</li>
<li>
<p>当请求响应后,我们需要从Map对象内移除自身的映射关系</p>
</li>
</ul><p>很清晰明了吧,大致上就是上边的流程了</p><p>我们按照设想,首先会有一个Map类型的变量,它用来保存我们的映射关系</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>url_method_paramsToReqMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>request</span><span>.</span></span><span>interceptors</span><span>.</span><span>request</span><span>.</span><span>use</span><span>(</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>(</span><span>config</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>repeatRequestHandle</span><span><span>(</span><span>config</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span><span> </span><span>config</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><span>error</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span><span>(</span><span>error</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>return</span><span><span> </span><span>error</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></code></pre><div><div></div><div></div></div></figure></div><p>可以看到我们在钩子里放了一个<code>repeatRequestHandle</code>函数,并将axios的config放了进去,这个函数最主要的作用就是用来判断是否是重复请求,我们来实现它,这里说下我是怎么判断是否是重复请求的,你们有更好的方式当然可以用更好的方式</p><p>判断是否是重复请求的依据是该次请求的路径方法和参数序列化后所形成的字符串是否一致,这里我们统称为请求的key,这已经可以保证绝大部分情况了,毕竟这些都完全一致的情况下,那么几乎是同意来源的请求;当然,如果的确不是同意来源的请求,但是又不想受到限制,可以自行加一些参数避开这一块逻辑即可,当然这是后话;</p><p>我们先来实现获取请求key的函数,这里我们用<a href="https://www.npmjs.com/package/qs" target="_blank">qs</a>来序列化参数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>genRequestKey</span><span> </span><span>=</span><span><span> (</span><span>config</span></span><span>:</span><span> </span><span>AxiosRequestConfig</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>url</span><span>, </span><span>method</span><span>, </span><span>params</span><span>, </span><span>data</span><span> </span><span>=</span><span> {} } </span><span>=</span><span><span> </span><span>config</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span><span> [</span><span>url</span><span>, </span><span>method</span><span>, </span><span>qs</span><span>.</span></span><span>stringify</span><span><span>(</span><span>params</span><span>), </span><span>qs</span><span>.</span></span><span>stringify</span><span><span>(</span><span>data</span><span>)].</span></span><span>join</span><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>好接下来就是判断逻辑了,很简单如果当前请求的key还在映射列表内,那么新请求就属于重复请求,反之就是符合放行条件的请求,这个方法还返回一个boolean来支撑外部的逻辑,比如进度条的<code>start</code>和<code>done</code>调用时机</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>repeatRequestHandle</span><span> </span><span>=</span><span><span> (</span><span>config</span></span><span>:</span><span> </span><span>AxiosRequestConfig</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>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span><span> (</span><span>url_method_paramsToReqMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>cancelRequest</span><span><span>(</span><span>config</span><span>, </span><span>key</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span> </span><span>false</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>else</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>addReqList</span><span><span>(</span><span>config</span><span>, </span><span>key</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>return</span><span> </span><span>true</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>cancelRequest</code>,<code>addReqList</code>,以下简述他们的主要作用:</p><ul>
<li>addReqList 主要用来处理放行请求的逻辑,它主要做两件事&lt;1&gt;.让取消函数和本次请求关联,2.将请求的key和请求取消函数放入映射列表</li>
<li>cancelRequest 用来取消该次请求</li>
</ul><p>解析来我们来实现他们,首先是addReqList</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>addReqList</span><span> </span><span>=</span><span> (</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>config</span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>config</span><span>.</span><span>cancelToken</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>axios</span><span>.</span></span><span>CancelToken</span><span><span>((</span><span>cancel</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>url_method_paramsToReqMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, </span><span>cancel</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>然后是cancelRequest,在这里你可以选择性的取消本次还是上次的请求,个人觉得最可靠的是取消新进入的请求,因为即使上次的请求未响应,但是极有可能在服务端已经做了一些操作,这个时候去取消明显很容易出问题,而且一些后端也要求前端不允许取消请求;当然也不是不行,经过我的测试,前端在取消请求后,后端也会有所反应,只要和后端商量好,在取消请求的时候做回滚操作那也不是行不通,只是做的越多出问题的概率也会更多.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>cancelRequest</span><span> </span><span>=</span><span> (</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>config</span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>config</span><span>.</span><span>cancelToken</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>axios</span><span>.</span></span><span>CancelToken</span><span><span>((</span><span>cancel</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>cancel</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>到这里,我们请求拦截这一块算是做完了,然后就是响应拦截器,这一块的逻辑很简单,在响应完成后直接移除映射关系即可</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>request</span><span>.</span></span><span>interceptors</span><span>.</span><span>response</span><span>.</span><span>use</span><span>(</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>(</span><span>response</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>     </span><span>removeReqList</span><span><span>(</span><span>response</span><span>.</span></span><span>config</span><span>);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>// TODO 业务处理</span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span><span> </span><span>response</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>err</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span><span>(</span><span>err</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></code></pre><div><div></div><div></div></div></figure></div><p>这里引入的<code>removeReqList</code>函数实现如下:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>removeReqList</span><span> </span><span>=</span><span> (</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>config</span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>url_method_paramsToReqMap</span><span>.</span><span>delete</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>cancelRequest</span><span><span>(</span><span>config</span><span>, </span><span>key</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>到这里我们的封装就算完成了,就可以开心的发送网络请求了<a href="#到这里我们的封装就算完成了就可以开心的发送网络请求了"><span>#</span></a></h2><p>完整的源码如下:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>axios</span><span>, { </span><span>AxiosRequestConfig</span><span> } </span><span>from</span><span> </span><span>"axios"</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>qs</span><span> </span><span>from</span><span> </span><span>"qs"</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>request</span><span> </span><span>=</span><span><span> </span><span>axios</span><span>.</span></span><span>create</span><span>({</span></div></div><div><div><div>5</div></div><div><span>  </span><span>baseURL</span><span>:</span><span> </span><span>"xxx"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>  </span><span>timeout</span><span>:</span><span> </span><span>2000</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>url_method_paramsToReqMap</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</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>const</span><span> </span><span>genRequestKey</span><span> </span><span>=</span><span><span> (</span><span>config</span></span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>13</div></div><div><span>  </span><span>const</span><span> { </span><span>url</span><span>, </span><span>method</span><span>, </span><span>params</span><span>, </span><span>data</span><span> </span><span>=</span><span> {} } </span><span>=</span><span><span> </span><span>config</span><span>;</span></span></div></div><div><div><div>14</div></div><div><span>  </span><span>return</span><span><span> [</span><span>url</span><span>, </span><span>method</span><span>, </span><span>qs</span><span>.</span></span><span>stringify</span><span><span>(</span><span>params</span><span>), </span><span>qs</span><span>.</span></span><span>stringify</span><span><span>(</span><span>data</span><span>)].</span></span><span>join</span><span>(</span><span>"_"</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>// 加入请求中列表</span></div></div><div><div><div>18</div></div><div><span>const</span><span> </span><span>addReqList</span><span> </span><span>=</span><span> (</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>config</span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>,</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>)</span></span></div></div><div><div><div>21</div></div><div><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>config</span><span>.</span><span>cancelToken</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>axios</span><span>.</span></span><span>CancelToken</span><span><span>((</span><span>cancel</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>url_method_paramsToReqMap</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, </span><span>cancel</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></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// 取消请求</span></div></div><div><div><div>28</div></div><div><span>const</span><span> </span><span>cancelRequest</span><span> </span><span>=</span><span> (</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>config</span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>,</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>)</span></span></div></div><div><div><div>31</div></div><div><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>config</span><span>.</span><span>cancelToken</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>axios</span><span>.</span></span><span>CancelToken</span><span><span>((</span><span>cancel</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>33</div></div><div><span>    </span><span>cancel</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></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>const</span><span> </span><span>removeReqList</span><span> </span><span>=</span><span> (</span></div></div><div><div><div>38</div></div><div><span><span>  </span></span><span>config</span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>,</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>)</span></span></div></div><div><div><div>40</div></div><div><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>url_method_paramsToReqMap</span><span>.</span><span>delete</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>42</div></div><div><span>  </span><span>cancelRequest</span><span><span>(</span><span>config</span><span>, </span><span>key</span><span>);</span></span></div></div><div><div><div>43</div></div><div><span>};</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>const</span><span> </span><span>repeatRequestHandle</span><span> </span><span>=</span><span><span> (</span><span>config</span></span><span>:</span><span> </span><span>AxiosRequestConfig</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>46</div></div><div><span>  </span><span>const</span><span> </span><span>key</span><span> </span><span>=</span><span> </span><span>genRequestKey</span><span><span>(</span><span>config</span><span>);</span></span></div></div><div><div><div>47</div></div><div><span>  </span><span>if</span><span><span> (</span><span>url_method_paramsToReqMap</span><span>.</span></span><span>has</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>48</div></div><div><span>    </span><span>cancelRequest</span><span><span>(</span><span>config</span><span>, </span><span>key</span><span>);</span></span></div></div><div><div><div>49</div></div><div><span>    </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>50</div></div><div><span><span>  </span></span><span>}</span><span>else</span><span> {</span></div></div><div><div><div>51</div></div><div><span>    </span><span>addReqList</span><span><span>(</span><span>config</span><span>, </span><span>key</span><span>);</span></span></div></div><div><div><div>52</div></div><div><span>    </span><span>return</span><span> </span><span>true</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><span>request</span><span>.</span></span><span>interceptors</span><span>.</span><span>request</span><span>.</span><span>use</span><span>(</span></div></div><div><div><div>57</div></div><div><span><span>  </span></span><span>(</span><span>config</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>58</div></div><div><span>    </span><span>repeatRequestHandle</span><span><span>(</span><span>config</span><span>);</span></span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>return</span><span><span> </span><span>config</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><span>error</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span><span>(</span><span>error</span><span>);</span></span></div></div><div><div><div>64</div></div><div><span>    </span><span>return</span><span><span> </span><span>error</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></div></div><div><div><div>67</div></div><div><span><span>request</span><span>.</span></span><span>interceptors</span><span>.</span><span>response</span><span>.</span><span>use</span><span>(</span></div></div><div><div><div>68</div></div><div><span><span>  </span></span><span>(</span><span>response</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>69</div></div><div><span>    </span><span>removeReqList</span><span><span>(</span><span>response</span><span>.</span></span><span>config</span><span>);</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>// TODO 业务处理</span></div></div><div><div><div>72</div></div><div>
</div></div><div><div><div>73</div></div><div><span>    </span><span>return</span><span><span> </span><span>response</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><span>  </span></span><span>(</span><span>err</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>76</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span><span>(</span><span>err</span><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></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://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-zhi-sheng-cheng-qi/</id>
      <title type="text">带你读红宝书之生成器</title>
      <published>2022-12-25T13:54:44.000Z</published>
      <updated>2022-12-25T13:54:44.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-zhi-sheng-cheng-qi/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>生成器是什么?<a href="#生成器是什么"><span>#</span></a></h2><p>生成器拥有在一个函数内暂停和恢复代码执行的能力,这里值得一说,我们熟悉的异步解决方案<code>async</code>,<code>await</code>就是基于生成器实现的.</p></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><span> </span><span>g</span><span>(){</span></div></div><div><div><div>2</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在function关键词之后跟上一个<code>*</code>号之后,那么这个函数就变成了一个生成器函数,在调用这个函数后,会得到一个生成器对象,这个对象实现了Iterator接口,所以具有迭代器的特性.</p><p>好的,接下来我们来看一个相对完整的生成器案例.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function*</span><span> </span><span>g</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>yield</span><span> (() </span><span>=&gt;</span><span><span> { </span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>11</span><span>); </span><span>return</span><span> </span><span>1</span><span> })()</span></div></div><div><div><div>3</div></div><div><span>  </span><span>yield</span><span> (() </span><span>=&gt;</span><span><span> { </span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>22</span><span>); </span><span>return</span><span> </span><span>2</span><span> })()</span></div></div><div><div><div>4</div></div><div><span>  </span><span>yield</span><span> (() </span><span>=&gt;</span><span><span> { </span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>33</span><span>); </span><span>return</span><span> </span><span>3</span><span> })()</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>gn</span><span> </span><span>=</span><span> </span><span>g</span><span>()</span></div></div><div><div><div>7</div></div><div><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>8</div></div><div><span>  </span><span>const</span><span> </span><span>val</span><span> </span><span>=</span><span><span> </span><span>gn</span><span>.</span></span><span>next</span><span>()</span></div></div><div><div><div>9</div></div><div><span>  </span><span>if</span><span><span> (</span><span>val</span><span>.</span></span><span>done</span><span>) </span><span>clearInterval</span><span><span>(</span><span>timer</span><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><span>val</span><span>);</span></span></div></div><div><div><div>11</div></div><div><span>}, </span><span>1000</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>总的来看,可以这样理解,生成器内遇到yield会暂停函数的执行,直到你调用next方法函数才会恢复执行,在遇到下一个yield时又会暂停执行,然后就是循环这个操作了.</p></section>
<section><h2>yield关键字还可以作为输入输出<a href="#yield关键字还可以作为输入输出"><span>#</span></a></h2><p>这个关键字可以让你每一次调用next时的入参在外部传入,而且next的返回值也可以有yield指定,使用方式也很简单.</p><p>来看输入</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><span> </span><span>gen</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>'start'</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>yield</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>yield</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>yield</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>const</span><span> </span><span>g</span><span> </span><span>=</span><span> </span><span>gen</span><span>()</span></div></div><div><div><div>9</div></div><div><span><span>g</span><span>.</span></span><span>next</span><span>()</span></div></div><div><div><div>10</div></div><div><span><span>g</span><span>.</span></span><span>next</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>11</div></div><div><span><span>g</span><span>.</span></span><span>next</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>12</div></div><div><span><span>g</span><span>.</span></span><span>next</span><span>(</span><span>3</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样我们就可以在外部传入参数来执行函数,这里注意第一次调用next不会获取你的入参,它的作用是让你的生成器函数开始执行.</p><p>接下来是输出:</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>function*</span><span> </span><span>gen</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>yield</span><span> </span><span>"嘿嘿嘿1"</span></div></div><div><div><div>4</div></div><div><span>  </span><span>yield</span><span> </span><span>"嘿嘿嘿2"</span></div></div><div><div><div>5</div></div><div><span>  </span><span>yield</span><span> </span><span>"嘿嘿嘿3"</span></div></div><div><div><div>6</div></div><div><span>  </span><span>yield*</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>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>const</span><span> </span><span>g</span><span> </span><span>=</span><span> </span><span>gen</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>g</span><span>.</span></span><span>next</span><span>().</span><span>value</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>g</span><span>.</span></span><span>next</span><span>().</span><span>value</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>g</span><span>.</span></span><span>next</span><span>().</span><span>value</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>g</span><span>.</span></span><span>next</span><span>().</span><span>value</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>g</span><span>.</span></span><span>next</span><span>().</span><span>value</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>g</span><span>.</span></span><span>next</span><span>().</span><span>value</span><span>);</span></div></div><div><div><div>15</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>g</span><span>.</span></span><span>next</span><span>().</span><span>value</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这就是yield输入输出的功能了;你可能注意到了,在上边的案例中,我们使用*来增强yield,这种方式可以让yield产生出可迭代对象,其实这和多写几行yield没什么区别.</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>P</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>values</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>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>[</span><span>Symbol</span><span>.</span></span><span>iterator</span><span>]() {</span></div></div><div><div><div>6</div></div><div><span>   </span><span>yield*</span><span> </span><span>this</span><span>.</span><span>values</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>wow,如此的简洁</p></section>
<section><h2>提前终止生成器<a href="#提前终止生成器"><span>#</span></a></h2><p>你可以直接在生成器对象上调用<code>return</code>这个api来提前终止,每个生成器对象上都会有这个api</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function*</span><span> </span><span>gen</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>yield</span><span> </span><span>"嘿嘿嘿1"</span></div></div><div><div><div>3</div></div><div><span>  </span><span>yield</span><span> </span><span>"嘿嘿嘿2"</span></div></div><div><div><div>4</div></div><div><span>  </span><span>yield</span><span> </span><span>"嘿嘿嘿3"</span></div></div><div><div><div>5</div></div><div><span>  </span><span>yield*</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>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>const</span><span> </span><span>g</span><span>=</span><span>gen</span><span>()</span></div></div><div><div><div>8</div></div><div><span><span>g</span><span>.</span></span><span>next</span><span>()</span></div></div><div><div><div>9</div></div><div><span><span>g</span><span>.</span></span><span>return</span><span>()</span></div></div><div><div><div>10</div></div><div><span>// g.next() 一旦生成器进入关闭状态是不能恢复的</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>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-zhi-die-dai-qi-mo-shi/</id>
      <title type="text">带你读红宝书之迭代器模式</title>
      <published>2022-12-21T16:39:13.000Z</published>
      <updated>2022-12-21T16:39:13.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-zhi-die-dai-qi-mo-shi/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么需要迭代器模式?<a href="#为什么需要迭代器模式"><span>#</span></a></h2><p>比如我们眼前有一个数组,[1,2,3,4,5],我们可以使用下边的代码中的for循环来轻易的进行循环迭代.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>arr</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></div></div><div><div><div>2</div></div><div><span>for</span><span>(</span><span>let</span><span><span> </span><span>i</span></span><span>=</span><span>0</span><span><span>;</span><span>i</span></span><span>&lt;</span><span><span>arr</span><span>.</span></span><span>length</span><span><span>;</span><span>i</span></span><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><span>arr</span><span>[</span><span>i</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>但其实这种方式存在了以下的隐式成本,我们不能明显感知是因为,我们对数组这种数据结构实在太熟悉了:</p><ul>
<li>我们必须知道我们将要进行循环迭代的是一个数组</li>
<li>我们在使用递增索引来循环迭代的前提下是已经知道了数组是满足递增索引这种规律模式的</li>
</ul><p>由于上边这两个隐式认知成本,我们可以发现如果我们在不知道数据结构类型和不知道数据的迭代规律,那么我们会根本不知道如何下手去循环这个数据.而迭代器模式j就是来解决这个问题的.可以这么理解,迭代器模式让我们可以用一种更通用的接口标准来循环迭代我们的数据.使用迭代器模式来迭代数据,我们不需要知道数据的详细结构,也不需要去摸清他们的规律,因为迭代器模式一般在一门语言里是一个比较通用的规则,我们只需要了解这部分内容就可以轻松的循环迭代我们的数据.根据迭代器接口的实现方式来看,迭代器模式理论上是可以循环迭代任何数据格式的,具体的看你如何去实现对应的接口.在js的世界里,已经有很大一部分内置数据实现了迭代器接口.其中包括:</p><ul>
<li>字符串</li>
<li>数组</li>
<li>映射</li>
<li>集合</li>
<li>arguments对象</li>
<li>DOM nodelist(这是一种类数组的数据结构)</li>
</ul><p>如果你要检查你的目标数据类型是否支持默认的迭代器,可以尝试将它的默认迭代器工厂函数暴露出来看看</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><span>obj</span><span>[</span><span>Symbol</span><span>.</span></span><span>iterator</span><span>]); </span><span>// undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>通常如果支持默认迭代器,那么暴露出来的值不应该是undefined,如果是undefined,那也说明是不支持默认迭代器的.
如果你的目标数据类型刚好是支持可迭代器协议的,那么你只需要调用这个工厂函数就可以得到一个迭代器,下边也展示一下可迭代协议下的数据的完整迭代过程:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>arr</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></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>it</span><span>=</span><span><span>arr</span><span>[</span><span>Symbol</span><span>.</span></span><span>iterator</span><span>]()</span></div></div><div><div><div>3</div></div><div><span>while</span><span>(</span><span>true</span><span>){</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>res</span><span>=</span><span><span>it</span><span>.</span></span><span>next</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>res</span><span>.</span></span><span>value</span><span>)</span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span><span>(</span><span>res</span><span>.</span></span><span>done</span><span>)</span><span>break</span><span>;</span><span>// 通过result中的done属性来判断是否应该终止迭代</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><ul>
<li>for-of</li>
<li>数组解构</li>
<li>拓展操作符 …</li>
<li>Array.from()</li>
<li>创建集合 new Set([1,2,3,4])</li>
<li>创建映射</li>
<li>Promise.all() Promise.race()</li>
<li>yield*操作符</li>
</ul><p>可以看到支持迭代器模式,是利大于弊的.</p></section>
<section><h2>迭代器协议<a href="#迭代器协议"><span>#</span></a></h2><p>首先我们来说明一下迭代器,迭代器是在你调用该数据的迭代器工厂函数返回给你的.他就是我们用来迭代数据的关键,在迭代器上有一个api.你只要调用一次,就会给你下一个被迭代的值,这个值被叫做IteratorResult;IteratorResult上有两个关键属性,done;前者表示该次迭代的值,done则表示是否完成了迭代.由此可知如果我们希望完整迭代我们的数据,那么我们需要源源不断的调用迭代器上的next方法,直到返回的IteratorResult上done属性为true.上边已经有代码展示了完整的迭代,这里就不再贴出代码了.</p><p>这里还有一个值得一说的地方,与迭代器形成绑定关系的只是数据的一个引用,并不是一个某个时刻的快照,或者一个完整的副本;如果在迭代过程没有完成的情况下,被迭代数据有了修改行为,那么依然会在迭代器的迭代中提现.</p></section>
<section><h2>自定义迭代器<a href="#自定义迭代器"><span>#</span></a></h2><p>既然我们知道了迭代器模式运行的原理,那我们是不是可以自己实现自己的迭代器呢?答案是当然可以.</p><p>我们首先有下边这个类,很明显他的实例不是一个可迭代协议的数据格式,而我们的工作就是让他支持可迭代协议</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>P</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>maxAge</span><span> </span><span>=</span><span> </span><span>100</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><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>P</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>maxAge</span><span> </span><span>=</span><span> </span><span>100</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><span>Symbol</span><span>.</span><span>iterator</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>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</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>return</span><span> {</span></div></div><div><div><div>11</div></div><div><span>      </span><span>next</span><span>:</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>i</span><span>++</span></div></div><div><div><div>13</div></div><div><span>        </span><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>this</span><span>.</span><span>maxAge</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>          </span><span>return</span><span> { </span><span>value</span><span>:</span><span> </span><span>this</span><span>.</span><span>maxAge</span><span>, </span><span>done</span><span>:</span><span> </span><span>true</span><span> }</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>16</div></div><div><span>          </span><span>return</span><span> { </span><span>value</span><span>:</span><span><span> </span><span>i</span><span>, </span></span><span>done</span><span>:</span><span> </span><span>false</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><p>我们可以对其进行测试</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>new</span><span> </span><span>P</span><span>()</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>it</span><span> </span><span>=</span><span><span> </span><span>p</span><span>[</span><span>Symbol</span><span>.</span></span><span>iterator</span><span>]()</span></div></div><div><div><div>3</div></div><div><span>while</span><span> (</span><span>true</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span><span> </span><span>it</span><span>.</span></span><span>next</span><span>()</span></div></div><div><div><div>5</div></div><div><span>  </span><span>if</span><span><span> (</span><span>res</span><span>.</span></span><span>done</span><span>) </span><span>break</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>res</span><span>.</span></span><span>value</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>如果你有尝试上边的代码,你会发现的确是正常工作的.我们会发现,要让数据在js世界里变成支持可迭代协议的数据,似乎没有想象中那么复杂.你可以在实现自己的迭代器后,尝试使用原生的特性来测试你的迭代器,比如下边的代码,一般都会预期工作</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for</span><span> (</span><span>const</span><span> </span><span>key</span><span> </span><span>of</span><span><span> </span><span>p</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>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><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>new</span><span> </span><span>Set</span><span>([</span><span>...</span><span><span>p</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>通常我们想要迭代停止下来,就只有等被迭代对象迭代耗尽为止,但是某些情况下我们拥有更多的选择,不必等对象迭代耗尽,就提前去终止他.那么我们可以在迭代器工厂函数返回的迭代器上添加一个<code>return</code>方法来达到我们想要的的效果,代码可以是下边的形状</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>P</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>maxAge</span><span> </span><span>=</span><span> </span><span>100</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><span>Symbol</span><span>.</span><span>iterator</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>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</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>return</span><span> {</span></div></div><div><div><div>11</div></div><div><span>      </span><span>next</span><span>:</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>i</span><span>++</span></div></div><div><div><div>13</div></div><div><span>        </span><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>this</span><span>.</span><span>maxAge</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>          </span><span>return</span><span> {  </span><span>done</span><span>:</span><span> </span><span>true</span><span> }</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>16</div></div><div><span>          </span><span>return</span><span> { </span><span>value</span><span>:</span><span><span> </span><span>i</span><span>, </span></span><span>done</span><span>:</span><span> </span><span>false</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>return</span><span>(){</span></div></div><div><div><div>20</div></div><div><span>        </span><span>// 为了防止next继续工作,我们需要做一些额外的工作</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>i</span><span>=</span><span>this</span><span>.</span><span>maxAge</span><span>+</span><span>1</span></div></div><div><div><div>22</div></div><div><span>        </span><span>// 然后返回一个IteratorResult对象即可</span></div></div><div><div><div>23</div></div><div><span>        </span><span>return</span><span> {</span><span>done</span><span>:</span><span>true</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>  </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>这个return api如果在js的一系列原生特性下是被自动调用的,比如遭遇异常,break等情况就会自行调用来提前终止迭代.</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>可以看到js迭代器并没有想象中那么复杂,它只是延展了对象的可迭代特性.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/ji-yi-ci-vue-zu-jian-xun-huan-yi-lai-wen-ti/</id>
      <title type="text">记一次Vue组件循环依赖问题</title>
      <published>2022-12-14T14:18:33.000Z</published>
      <updated>2022-12-14T14:18:33.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/ji-yi-ci-vue-zu-jian-xun-huan-yi-lai-wen-ti/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>起因<a href="#起因"><span>#</span></a></h2><p>事情是这样的,在工作中由于业务的演进,一个模块有了二级业务,这个任务交到了我,然后我发现一级和二级的视图和业务逻辑很相似,只是存在一些细微的区别;那这样最好了,可以直接复用一级业务的组件,然后我给这个组件定义一些props来做区分是哪一级的业务即可,合理,开干.</p></section>
<section><h2>遇到的问题<a href="#遇到的问题"><span>#</span></a></h2><p>就在我开开心心开发的时候,控制台突然给我抛出来一个错:</p><blockquote><p>[vue warn]: unknown custom element: XXX - did you register the component correctly? for recursive components, make sure to provide the “name” option.</p></blockquote><p>没注册吗?好的,我去看一下.但是在页面里明明是注册了的,刷新了页面还是报错,我又想到会不会是拼写错误.仔细检查了下来,也没有拼写错误.然后我猛地想起,以前好像遇到过这个问题,是组件循环依赖了.官网对此有明确说明</p><p>主要原因:</p><blockquote><p>为了解释这里发生了什么，我们先把两个组件称为 A 和 B。模块系统发现它需要 A，但是首先 A 依赖 B，但是 B 又依赖 A，但是 A 又依赖 B，如此往复。这变成了一个循环，不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题，我们需要给模块系统一个点，在那里“A 反正是需要 B 的，但是我们不需要先解析 B。”</p></blockquote></section>
<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>beforeCreate</span><span>: </span></span><span>function</span><span> () {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>this</span><span>.</span><span>$options</span><span>.</span><span>components</span><span>.</span><span>TreeFolderContents</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'./tree-folder-contents.vue'</span><span>).</span><span>default</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>components</span><span>: {</span></span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>TreeFolderContents</span><span>: () </span><span>=&gt;</span><span> </span><span>import</span><span>(</span><span>'./tree-folder-contents.vue'</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>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/leetcode-shua-ti-ri-ji/</id>
      <title type="text">leetcode刷题日记</title>
      <published>2022-11-13T12:04:01.000Z</published>
      <updated>2022-11-13T12:04:01.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/leetcode-shua-ti-ri-ji/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>2022/11/13 #20 有效的括号<a href="#20221113-20-有效的括号"><span>#</span></a></h2><p>这道题目的核心思想理解栈的数据结构</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>isValid</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>s</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>dict</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>[</span><span>"("</span><span>, </span><span>")"</span><span>],</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>"{"</span><span>, </span><span>"}"</span><span>],</span></div></div><div><div><div>5</div></div><div><span><span>    </span></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>])</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> </span><span>stack</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>pres</span><span> </span><span>=</span><span> [</span><span>...</span><span><span>dict</span><span>.</span></span><span>keys</span><span>()]</span></div></div><div><div><div>9</div></div><div><span>  </span><span>const</span><span> </span><span>ends</span><span> </span><span>=</span><span> [</span><span>...</span><span><span>dict</span><span>.</span></span><span>values</span><span>()]</span></div></div><div><div><div>10</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>s</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>=</span><span><span> </span><span>s</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span><span> (</span><span>pres</span><span>.</span></span><span>includes</span><span><span>(</span><span>char</span><span>)) {</span></span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>stack</span><span>.</span><span>push</span><span><span>(</span><span>char</span><span>)</span></span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>ends</span><span>.</span></span><span>includes</span><span><span>(</span><span>char</span><span>)) {</span></span></div></div><div><div><div>15</div></div><div><span>      </span><span>if</span><span><span> (</span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>stack</span><span>.</span></span><span>pop</span><span>()) </span><span>!==</span><span><span> </span><span>char</span><span>) </span></span><span>return</span><span> </span><span>false</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>return</span><span> </span><span>!</span><span><span>stack</span><span>.</span></span><span>length</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></section>
<section><h2>2022/11/14 #1 两数之和<a href="#20221114-1-两数之和"><span>#</span></a></h2><p>两个数字相亲记~</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>twoSum</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums</span><span>, </span><span>target</span><span>) {</span></span></div></div><div><div><div>2</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>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>nums</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>n</span><span> </span><span>=</span><span><span> </span><span>nums</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>theHalf</span><span> </span><span>=</span><span><span> </span><span>target</span><span> </span></span><span>-</span><span><span> </span><span>n</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>map</span><span>.</span></span><span>has</span><span><span>(</span><span>theHalf</span><span>)) {</span></span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>map</span><span>.</span><span>set</span><span><span>(</span><span>n</span><span>, </span><span>i</span><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>return</span><span><span> [</span><span>map</span><span>.</span></span><span>get</span><span><span>(</span><span>theHalf</span><span>), </span><span>i</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></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>2022/11/15 #9 回文数<a href="#20221115-9-回文数"><span>#</span></a></h2><p>其实就是两个指针靠近,直到两人亲上都还能没发生不同,那就是一个回文数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>isPalindrome</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>x</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>n</span><span> </span><span>=</span><span> </span><span>""</span><span> </span><span>+</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span><span> (</span><span>n</span><span>.</span></span><span>length</span><span> </span><span>===</span><span> </span><span>1</span><span>) </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>4</div></div><div><span>  </span><span>let</span><span><span> </span><span>p1</span><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>p2</span><span> </span><span>=</span><span><span> </span><span>n</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>6</div></div><div><span>  </span><span>while</span><span><span> (</span><span>p1</span></span><span>++</span><span> </span><span>&lt;</span><span><span> </span><span>p2</span></span><span>--</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span><span> (</span><span>n</span><span>[</span><span>p1</span></span><span>-</span><span>1</span><span>] </span><span>!==</span><span><span> </span><span>n</span><span>[</span><span>p2</span></span><span>+</span><span>1</span><span>]) </span><span>return</span><span> </span><span>false</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>true</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><h2>2022/11/16 #13 罗马数字<a href="#20221116-13-罗马数字"><span>#</span></a></h2><p>这道题我最开始写出来是这样的,能明显感觉到不是很优雅</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>romanToInt</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>s</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>dict</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>[</span><span>'I'</span><span>, </span><span>1</span><span>],</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>'V'</span><span>, </span><span>5</span><span>],</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>[</span><span>'X'</span><span>, </span><span>10</span><span>],</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>[</span><span>'L'</span><span>, </span><span>50</span><span>],</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>[</span><span>'C'</span><span>, </span><span>100</span><span>],</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>[</span><span>'D'</span><span>, </span><span>500</span><span>],</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>[</span><span>'M'</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><span>  </span><span>let</span><span><span> </span><span>flag</span><span> </span></span><span>=</span><span> </span><span>false</span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> </span><span>nums</span><span> </span><span>=</span><span><span> </span><span>s</span><span>.</span></span><span>split</span><span>(</span><span>''</span><span>)</span></div></div><div><div><div>13</div></div><div><span>  </span><span>return</span><span><span> </span><span>nums</span><span>.</span></span><span>reduce</span><span><span>((</span><span>p</span><span>, </span><span>n</span><span>, </span><span>i</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>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>nums</span><span>[</span><span>i</span><span> </span></span><span>+</span><span> </span><span>1</span><span>]) </span><span>&gt;</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>n</span><span>)) {</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>flag</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>16</div></div><div><span>      </span><span>return</span><span><span> </span><span>p</span><span> </span></span><span>+</span><span> </span><span>0</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>if</span><span><span> (</span><span>flag</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>flag</span><span> </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>20</div></div><div><span>      </span><span>return</span><span><span> </span><span>p</span><span> </span></span><span>+</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>n</span><span>) </span></span><span>-</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>nums</span><span>[</span><span>i</span><span> </span></span><span>-</span><span> </span><span>1</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>return</span><span><span> </span><span>p</span><span> </span></span><span>+</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>n</span><span>)</span></span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>}, </span><span>0</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><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>romanToInt</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>s</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>dict</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>([</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>[</span><span>'I'</span><span>, </span><span>1</span><span>],</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>'V'</span><span>, </span><span>5</span><span>],</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>[</span><span>'X'</span><span>, </span><span>10</span><span>],</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>[</span><span>'L'</span><span>, </span><span>50</span><span>],</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>[</span><span>'C'</span><span>, </span><span>100</span><span>],</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>[</span><span>'D'</span><span>, </span><span>500</span><span>],</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>[</span><span>'M'</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><span>  </span><span>return</span><span><span> </span><span>s</span><span>.</span></span><span>split</span><span>(</span><span>''</span><span>).</span><span>reduce</span><span><span>((</span><span>p</span><span>, </span><span>n</span><span>, </span><span>i</span><span>, </span><span>ns</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span><span> (</span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>ns</span><span>[</span><span>i</span><span> </span></span><span>+</span><span> </span><span>1</span><span>]) </span><span>&gt;</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>n</span><span>)) {</span></span></div></div><div><div><div>13</div></div><div><span>      </span><span>return</span><span><span> </span><span>p</span><span> </span></span><span>-</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>n</span><span>)</span></span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>15</div></div><div><span>      </span><span>return</span><span><span> </span><span>p</span><span> </span></span><span>+</span><span><span> </span><span>dict</span><span>.</span></span><span>get</span><span><span>(</span><span>n</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><span>0</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>2022/11/17 #14 最长公共前缀<a href="#20221117-14-最长公共前缀"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>longestCommonPrefix</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>strs</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>p</span><span> </span></span><span>=</span><span> </span><span>0</span></div></div><div><div><div>3</div></div><div><span>  </span><span>while</span><span><span> (</span><span>p</span><span> </span></span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>uniPicks</span><span> </span><span>=</span><span> [</span><span>...</span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>strs</span><span>.</span></span><span>map</span><span><span>((</span><span>str</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>str</span><span>[</span><span>p</span><span>]))]</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span><span> (</span><span>uniPicks</span><span>.</span></span><span>includes</span><span>(</span><span>undefined</span><span>) </span><span>||</span><span><span> </span><span>uniPicks</span><span>.</span></span><span>length</span><span> </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>return</span><span><span> </span><span>strs</span><span>[</span></span><span>0</span><span>].</span><span>substring</span><span>(</span><span>0</span><span><span>, </span><span>p</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>p</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></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>2022/11/18 #26 删除有序数组中的重复项<a href="#20221118-26-删除有序数组中的重复项"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>removeDuplicates</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>uniNums</span><span> </span><span>=</span><span> [</span><span>...</span><span>new</span><span> </span><span>Set</span><span><span>(</span><span>nums</span><span>)]</span></span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>nums</span><span>.</span><span>length</span><span> </span><span>=</span><span><span> </span><span>uniNums</span><span>.</span></span><span>length</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>uniNums</span><span>.</span><span>forEach</span><span><span>((</span><span>n</span><span>, </span><span>i</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>nums</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>n</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>return</span><span><span> </span><span>nums</span><span>.</span></span><span>length</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><h2>2022/11/19 #27 移除元素<a href="#20221119-27-移除元素"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>removeElement</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums</span><span>, </span><span>val</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>nums</span><span>.</span></span><span>length</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span><span> (</span><span>nums</span><span>[</span><span>i</span><span>] </span></span><span>===</span><span><span> </span><span>val</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>nums</span><span>.</span><span>splice</span><span><span>(</span><span>i</span><span>, </span></span><span>1</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></span><span>i</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>return</span><span><span> </span><span>nums</span><span>.</span></span><span>length</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><h2>2022/11/21 #35 搜索插入位置 | #58 最后一个单词的长度<a href="#20221121-35-搜索插入位置--58-最后一个单词的长度"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// #35 搜索插入位置</span></div></div><div><div><div>2</div></div><div><span>var</span><span> </span><span>searchInsert</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums</span><span>, </span><span>target</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>nums</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>// case1 相等</span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span><span> (</span><span>nums</span><span>[</span><span>i</span><span>] </span></span><span>===</span><span><span> </span><span>target</span><span>) </span></span><span>return</span><span><span> </span><span>i</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>// case2 在某个节点大于它之前</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span><span> (</span><span>nums</span><span>[</span><span>i</span><span>] </span></span><span>&gt;</span><span><span> </span><span>target</span><span>) </span></span><span>return</span><span><span> </span><span>i</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>// case3 插入末尾</span></div></div><div><div><div>10</div></div><div><span>  </span><span>return</span><span><span> </span><span>nums</span><span>.</span></span><span>length</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>// #58 最后一个单词的长度</span></div></div><div><div><div>14</div></div><div><span>var</span><span> </span><span>lengthOfLastWord</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>s</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>  </span><span>return</span><span><span> </span><span>s</span><span>.</span></span><span>trim</span><span>().</span><span>split</span><span>(</span><span>" "</span><span>).</span><span>at</span><span>(</span><span>-</span><span>1</span><span>).</span><span>length</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>2022/11/22 #66 加一<a href="#20221122-66-加一"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>plusOne</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>digits</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>carry</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></span><span>digits</span><span>[</span><span>digits</span><span>.</span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span>] </span><span>=</span><span><span> </span><span>digits</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>) </span><span>+</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>4</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><span>digits</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span><span>; </span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span><span>; </span><span>i</span></span><span>--</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>let</span><span><span> </span><span>n</span><span> </span></span><span>=</span><span><span> </span><span>digits</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span><span> (</span><span>carry</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>n</span><span>++</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>carry</span><span> </span><span>=</span><span> </span><span>false</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>if</span><span><span> (</span><span>n</span><span> </span></span><span>&gt;=</span><span> </span><span>10</span><span>) {</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>carry</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>digits</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>n</span><span> </span></span><span>%</span><span> </span><span>10</span><span>;</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><span>digits</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>n</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>if</span><span><span> (</span><span>carry</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>digits</span><span>.</span><span>unshift</span><span>(</span><span>1</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>return</span><span><span> </span><span>digits</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>想了想,其实所有工作都可以在for循环里完成</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>plusOne</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>digits</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>digits</span><span>[</span><span>digits</span><span>.</span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span>] </span><span>=</span><span><span> </span><span>digits</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>) </span><span>+</span><span> </span><span>1</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><span>digits</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</span><span><span>; </span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span><span>; </span><span>i</span></span><span>--</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span><span> (</span><span>digits</span><span>[</span><span>i</span><span>] </span></span><span>&gt;=</span><span> </span><span>10</span><span>) {</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>digits</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>digits</span><span>[</span><span>i</span><span>] </span></span><span>%</span><span> </span><span>10</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>if</span><span><span> (</span><span>i</span><span> </span></span><span>===</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>digits</span><span>.</span><span>unshift</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>digits</span><span>[</span><span>i</span><span> </span><span>-</span><span> </span><span>1</span><span>] </span><span>=</span><span><span> </span><span>digits</span><span>[</span><span>i</span><span> </span></span><span>-</span><span> </span><span>1</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><span><span>  </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>  </span><span>return</span><span><span> </span><span>digits</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></section>
<section><h2>2022/11/23 #67 二进制求和<a href="#20221123-67-二进制求和"><span>#</span></a></h2><p>这道题和昨天的题神似,不过换成二进制的n+n,昨天是十进制的n+1</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>addBinary</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>r</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>sameLen</span><span> </span><span>=</span><span><span> </span><span>a</span><span>.</span></span><span>length</span><span> </span><span>===</span><span><span> </span><span>b</span><span>.</span></span><span>length</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>const</span><span> </span><span>l</span><span> </span><span>=</span><span><span> </span><span>sameLen</span><span> </span></span><span>?</span><span><span> </span><span>a</span><span> </span></span><span>:</span><span><span> </span><span>a</span><span>.</span></span><span>length</span><span> </span><span>&gt;</span><span><span> </span><span>b</span><span>.</span></span><span>length</span><span> </span><span>?</span><span><span> </span><span>a</span><span> </span></span><span>:</span><span><span> </span><span>b</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>lt</span><span> </span><span>=</span><span><span> </span><span>l</span><span>.</span></span><span>split</span><span>(</span><span>""</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> </span><span>li</span><span> </span><span>=</span><span><span> </span><span>lt</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</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>const</span><span> </span><span>s</span><span> </span><span>=</span><span><span> </span><span>sameLen</span><span> </span></span><span>?</span><span><span> </span><span>b</span><span> </span></span><span>:</span><span><span> </span><span>a</span><span>.</span></span><span>length</span><span> </span><span>&lt;</span><span><span> </span><span>b</span><span>.</span></span><span>length</span><span> </span><span>?</span><span><span> </span><span>a</span><span> </span></span><span>:</span><span><span> </span><span>b</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> </span><span>st</span><span> </span><span>=</span><span><span> </span><span>s</span><span>.</span></span><span>split</span><span>(</span><span>""</span><span>);</span></div></div><div><div><div>11</div></div><div><span>  </span><span>let</span><span><span> </span><span>si</span><span> </span></span><span>=</span><span><span> </span><span>st</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</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>const</span><span> </span><span>di</span><span> </span><span>=</span><span><span> </span><span>li</span><span> </span></span><span>-</span><span><span> </span><span>si</span><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>while</span><span><span> (</span><span>si</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>const</span><span> </span><span>n</span><span> </span><span>=</span><span> </span><span>+</span><span><span>lt</span><span>[</span><span>di</span><span> </span></span><span>+</span><span><span> </span><span>si</span><span>] </span></span><span>+</span><span> </span><span>+</span><span><span>st</span><span>[</span><span>si</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>if</span><span><span> (</span><span>n</span><span> </span></span><span>&gt;=</span><span> </span><span>2</span><span>) {</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>r</span><span>.</span><span>unshift</span><span><span>(</span><span>n</span><span> </span></span><span>%</span><span> </span><span>2</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>if</span><span><span> (</span><span>si</span><span> </span></span><span>===</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>sameLen</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>r</span><span>.</span><span>unshift</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>lt</span><span>[</span><span>si</span><span> </span><span>+</span><span><span> </span><span>di</span><span> </span></span><span>-</span><span> </span><span>1</span><span>] </span><span>=</span><span> </span><span>+</span><span><span>lt</span><span>[</span><span>si</span><span> </span></span><span>+</span><span><span> </span><span>di</span><span> </span></span><span>-</span><span> </span><span>1</span><span>] </span><span>+</span><span> </span><span>1</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></div></div><div><div><div>28</div></div><div><span>    </span><span>else</span><span> {</span></div></div><div><div><div>29</div></div><div><span><span>      </span></span><span>r</span><span>.</span><span>unshift</span><span><span>(</span><span>n</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>si</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></div></div><div><div><div>34</div></div><div><span>  </span><span>if</span><span><span> (</span><span>di</span><span>) {</span></span></div></div><div><div><div>35</div></div><div><span>    </span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span><span> </span><span>di</span><span> </span></span><span>-</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>36</div></div><div><span>    </span><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>37</div></div><div><span>      </span><span>const</span><span> </span><span>n</span><span> </span><span>=</span><span><span> </span><span>lt</span><span>[</span><span>i</span><span>];</span></span></div></div><div><div><div>38</div></div><div><span>      </span><span>if</span><span><span> (</span><span>n</span><span> </span></span><span>&gt;=</span><span> </span><span>2</span><span>) {</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>r</span><span>.</span><span>unshift</span><span><span>(</span><span>n</span><span> </span></span><span>%</span><span> </span><span>2</span><span>)</span></div></div><div><div><div>40</div></div><div><span>        </span><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>===</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>41</div></div><div><span><span>          </span></span><span>r</span><span>.</span><span>unshift</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>43</div></div><div><span><span>          </span></span><span>lt</span><span>[</span><span>i</span><span> </span><span>-</span><span> </span><span>1</span><span>] </span><span>=</span><span> </span><span>+</span><span><span>lt</span><span>[</span><span>i</span><span> </span></span><span>-</span><span> </span><span>1</span><span>] </span><span>+</span><span> </span><span>1</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><span>else</span><span> {</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>r</span><span>.</span><span>unshift</span><span><span>(</span><span>n</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></span><span>i</span><span>--</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>return</span><span><span> </span><span>r</span><span>.</span></span><span>join</span><span>(</span><span>''</span><span>);</span></div></div><div><div><div>52</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>var</span><span> </span><span>addBinary</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>r</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>sameLen</span><span> </span><span>=</span><span><span> </span><span>a</span><span>.</span></span><span>length</span><span> </span><span>===</span><span><span> </span><span>b</span><span>.</span></span><span>length</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>const</span><span> </span><span>l</span><span> </span><span>=</span><span><span> </span><span>sameLen</span><span> </span></span><span>?</span><span><span> </span><span>a</span><span> </span></span><span>:</span><span><span> </span><span>a</span><span>.</span></span><span>length</span><span> </span><span>&gt;</span><span><span> </span><span>b</span><span>.</span></span><span>length</span><span> </span><span>?</span><span><span> </span><span>a</span><span> </span></span><span>:</span><span><span> </span><span>b</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>lt</span><span> </span><span>=</span><span><span> </span><span>l</span><span>.</span></span><span>split</span><span>(</span><span>""</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>let</span><span><span> </span><span>li</span><span> </span></span><span>=</span><span><span> </span><span>lt</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</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>const</span><span> </span><span>s</span><span> </span><span>=</span><span><span> </span><span>sameLen</span><span> </span></span><span>?</span><span><span> </span><span>b</span><span> </span></span><span>:</span><span><span> </span><span>a</span><span>.</span></span><span>length</span><span> </span><span>&lt;</span><span><span> </span><span>b</span><span>.</span></span><span>length</span><span> </span><span>?</span><span><span> </span><span>a</span><span> </span></span><span>:</span><span><span> </span><span>b</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> </span><span>st</span><span> </span><span>=</span><span><span> </span><span>s</span><span>.</span></span><span>split</span><span>(</span><span>""</span><span>);</span></div></div><div><div><div>11</div></div><div><span>  </span><span>const</span><span> </span><span>si</span><span> </span><span>=</span><span><span> </span><span>st</span><span>.</span></span><span>length</span><span> </span><span>-</span><span> </span><span>1</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>const</span><span> </span><span>di</span><span> </span><span>=</span><span><span> </span><span>li</span><span> </span></span><span>-</span><span><span> </span><span>si</span><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>const</span><span> </span><span>ft</span><span> </span><span>=</span><span><span> </span><span>sameLen</span><span> </span></span><span>?</span><span><span> </span><span>st</span><span> </span></span><span>:</span><span> [</span><span>...</span><span>new</span><span> </span><span>Array</span><span><span>(</span><span>di</span><span>).</span></span><span>fill</span><span>(</span><span>0</span><span>), </span><span>...</span><span><span>st</span><span>];</span></span></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>while</span><span><span> (</span><span>li</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>const</span><span> </span><span>n</span><span> </span><span>=</span><span> </span><span>+</span><span><span>lt</span><span>[</span><span>li</span><span>] </span></span><span>+</span><span> </span><span>+</span><span><span>ft</span><span>[</span><span>li</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>if</span><span><span> (</span><span>n</span><span> </span></span><span>&gt;=</span><span> </span><span>2</span><span>) {</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>r</span><span>.</span><span>unshift</span><span><span>(</span><span>n</span><span> </span></span><span>%</span><span> </span><span>2</span><span>);</span></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>if</span><span><span> (</span><span>li</span><span> </span></span><span>===</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>r</span><span>.</span><span>unshift</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>25</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>lt</span><span>[</span><span>li</span><span> </span><span>-</span><span> </span><span>1</span><span>] </span><span>=</span><span> </span><span>+</span><span><span>lt</span><span>[</span><span>li</span><span> </span></span><span>-</span><span> </span><span>1</span><span>] </span><span>+</span><span> </span><span>1</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>// 无需进位</span></div></div><div><div><div>30</div></div><div><span>    </span><span>else</span><span> {</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>r</span><span>.</span><span>unshift</span><span><span>(</span><span>n</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>li</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>return</span><span><span> </span><span>r</span><span>.</span></span><span>join</span><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><p>确实少了很多代码哈.</p></section>
<section><h2>2022/11/27 #69 X的平方根 | #70 爬楼梯 | # 83 删除排序链表中的重复元素<a href="#20221127-69-x的平方根--70-爬楼梯---83-删除排序链表中的重复元素"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// #69</span></div></div><div><div><div>2</div></div><div><span>var</span><span> </span><span>mySqrt</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>x</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span><span> (</span><span>x</span><span> </span></span><span>==</span><span> </span><span>0</span><span> </span><span>||</span><span><span> </span><span>x</span><span> </span></span><span>==</span><span> </span><span>1</span><span>) </span><span>return</span><span><span> </span><span>x</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>let</span><span><span> </span><span>left</span><span> </span></span><span>=</span><span> </span><span>1</span><span><span>, </span><span>right</span><span> </span></span><span>=</span><span><span> </span><span>x</span><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>while</span><span><span> (</span><span>left</span><span> </span></span><span>&lt;</span><span><span> </span><span>right</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>// 如果只相隔1,直接返回左边</span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span><span> (</span><span>right</span><span> </span></span><span>-</span><span><span> </span><span>left</span><span> </span></span><span>==</span><span> </span><span>1</span><span>) </span><span>return</span><span><span> </span><span>left</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>let</span><span><span> </span><span>mid</span><span> </span></span><span>=</span><span><span> </span><span>left</span><span> </span></span><span>+</span><span><span> </span><span>Math</span><span>.</span></span><span>floor</span><span><span>((</span><span>right</span><span> </span></span><span>-</span><span><span> </span><span>left</span><span>) </span></span><span>/</span><span> </span><span>2</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>let</span><span><span> </span><span>res</span><span> </span></span><span>=</span><span><span> </span><span>x</span><span> </span></span><span>/</span><span><span> </span><span>mid</span><span>;</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 如果刚好结果相等,中位数就是想要的数字</span></div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span><span> (</span><span>res</span><span> </span></span><span>===</span><span><span> </span><span>mid</span><span>) </span></span><span>return</span><span><span> </span><span>mid</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>if</span><span><span> (</span><span>res</span><span> </span></span><span>&lt;</span><span><span> </span><span>mid</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>right</span><span> </span><span>=</span><span><span> </span><span>mid</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></div></div><div><div><div>20</div></div><div><span>    </span><span>else</span><span> {</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>left</span><span> </span><span>=</span><span><span> </span><span>mid</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><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>// #70 这是一道经典的动态规划的算法题目,你需要做的最关键的一点是总结出前后节点的规律,或者说是一个公式</span></div></div><div><div><div>27</div></div><div><span>var</span><span> </span><span>climbStairs</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>n</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span>  </span><span>if</span><span><span> (</span><span>n</span><span> </span></span><span>&lt;</span><span> </span><span>2</span><span>) </span><span>return</span><span> </span><span>1</span><span>;</span></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>const</span><span> </span><span>dp</span><span> </span><span>=</span><span> [</span><span>1</span><span>, </span><span>1</span><span>];</span></div></div><div><div><div>31</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>2</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;=</span><span><span> </span><span>n</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>dp</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>dp</span><span>[</span><span>i</span><span> </span></span><span>-</span><span> </span><span>1</span><span>] </span><span>+</span><span><span> </span><span>dp</span><span>[</span><span>i</span><span> </span></span><span>-</span><span> </span><span>2</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>return</span><span><span> </span><span>dp</span><span>[</span><span>n</span><span>];</span></span></div></div><div><div><div>35</div></div><div><span>};</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>// #83</span></div></div><div><div><div>38</div></div><div><span>var</span><span> </span><span>deleteDuplicates</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>head</span><span>) {</span></span></div></div><div><div><div>39</div></div><div><span>  </span><span>let</span><span><span> </span><span>p</span><span> </span></span><span>=</span><span><span> </span><span>head</span><span>;</span></span></div></div><div><div><div>40</div></div><div><span>  </span><span>while</span><span><span> (</span><span>p</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>p</span><span>.</span></span><span>next</span><span>) {</span></div></div><div><div><div>41</div></div><div><span>    </span><span>// 下个节点</span></div></div><div><div><div>42</div></div><div><span>    </span><span>const</span><span> </span><span>next</span><span> </span><span>=</span><span><span> </span><span>p</span><span>.</span></span><span>next</span><span>;</span></div></div><div><div><div>43</div></div><div><span>    </span><span>// 如果重复,将当前节点的next指向下个节点的next</span></div></div><div><div><div>44</div></div><div><span>    </span><span>if</span><span><span> (</span><span>next</span><span>.</span></span><span>val</span><span> </span><span>===</span><span><span> </span><span>p</span><span>.</span></span><span>val</span><span>) {</span></div></div><div><div><div>45</div></div><div><span><span>      </span></span><span>p</span><span>.</span><span>next</span><span> </span><span>=</span><span><span> </span><span>next</span><span>.</span></span><span>next</span><span>;</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>47</div></div><div><span>      </span><span>// 将当前指针往后移动</span></div></div><div><div><div>48</div></div><div><span><span>      </span></span><span>p</span><span> </span><span>=</span><span><span> </span><span>p</span><span>.</span></span><span>next</span><span>;</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>return</span><span><span> </span><span>head</span><span>;</span></span></div></div><div><div><div>52</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>2023/1/19 #88 合并两个有序数组<a href="#2023119-88-合并两个有序数组"><span>#</span></a></h2><p>这个题依然算是简单的指针题,两个指针在自己的合适的情况前进,直到走到目标位置即可</p><p>不知道为啥今天leetcode抽疯测试和提交出问题了,但是在本地测试是没问题的,没有测试所有测试用例,可能会有边角料问题,小case~</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>merge</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums1</span><span>, </span><span>m</span><span>, </span><span>nums2</span><span>, </span><span>n</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>3</div></div><div><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>j</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div><span>  </span><span>while</span><span><span> (</span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>m</span><span> </span></span><span>||</span><span><span> </span><span>j</span><span> </span></span><span>&lt;</span><span><span> </span><span>n</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>n1</span><span> </span><span>=</span><span><span> </span><span>nums1</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>n2</span><span> </span><span>=</span><span><span> </span><span>nums2</span><span>[</span><span>j</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><span>if</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;=</span><span><span> </span><span>m</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>push</span><span><span>(</span><span>n2</span><span>)</span></span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>j</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></div></div><div><div><div>13</div></div><div><span>    </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>j</span><span> </span></span><span>&gt;=</span><span><span> </span><span>n</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>push</span><span><span>(</span><span>n1</span><span>)</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>i</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>else</span><span> </span><span>if</span><span><span> (</span><span>n1</span><span> </span></span><span>&lt;</span><span><span> </span><span>n2</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>push</span><span><span>(</span><span>n1</span><span>)</span></span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>i</span><span>++</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>n1</span><span> </span></span><span>&gt;</span><span><span> </span><span>n2</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>push</span><span><span>(</span><span>n2</span><span>)</span></span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>j</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>else</span><span> {</span></div></div><div><div><div>27</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>push</span><span><span>(</span><span>n1</span><span>, </span><span>n2</span><span>)</span></span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>i</span><span>++</span></div></div><div><div><div>29</div></div><div><span><span>      </span></span><span>j</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><span>return</span><span><span> </span><span>res</span></span></div></div><div><div><div>33</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>原来leetcode那天并没有抽风,其实是我没注意到题目,是合并到num1数组,不是返回一个新数组 =.=</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>merge</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums1</span><span>, </span><span>m</span><span>, </span><span>nums2</span><span>, </span><span>n</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><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span><span> </span><span>m</span><span> </span></span><span>-</span><span> </span><span>1</span><span><span>, </span><span>j</span><span> </span></span><span>=</span><span><span> </span><span>n</span><span> </span></span><span>-</span><span> </span><span>1</span><span><span>, </span><span>k</span><span> </span></span><span>=</span><span><span> </span><span>m</span><span> </span></span><span>+</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>while</span><span><span> (</span><span>i</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>j</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>if</span><span><span> (</span><span>nums1</span><span>[</span><span>i</span><span>] </span></span><span>&gt;</span><span><span> </span><span>nums2</span><span>[</span><span>j</span><span>]) {</span></span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>nums1</span><span>[</span><span>k</span><span>--</span><span>] </span><span>=</span><span><span> </span><span>nums1</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>} </span><span>else</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>nums1</span><span>[</span><span>k</span><span>--</span><span>] </span><span>=</span><span><span> </span><span>nums2</span><span>[</span><span>j</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>// nums2还有剩余</span></div></div><div><div><div>12</div></div><div><span>  </span><span>while</span><span><span> (</span><span>j</span><span> </span></span><span>&gt;=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>nums1</span><span>[</span><span>k</span><span>--</span><span>] </span><span>=</span><span><span> </span><span>nums2</span><span>[</span><span>j</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>return</span><span><span> </span><span>nums1</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><h2>2023/1/19 #112 合并两个有序数组<a href="#2023119-112-合并两个有序数组"><span>#</span></a></h2></section>
<section><h2>2023/4/11 #104 二叉树的最大深度<a href="#2023411-104-二叉树的最大深度"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>maxDepth</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>root</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>if</span><span><span> (</span><span>root</span><span> </span></span><span>===</span><span> </span><span>null</span><span>) </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span><span> </span><span>Math</span><span>.</span></span><span>max</span><span>(</span><span>maxDepth</span><span><span>(</span><span>root</span><span>.</span></span><span>left</span><span>), </span><span>maxDepth</span><span><span>(</span><span>root</span><span>.</span></span><span>right</span><span>)) </span><span>+</span><span> </span><span>1</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><h2>20230/6/7<a href="#2023067"><span>#</span></a></h2><section><h3>189 轮转数组<a href="#189-轮转数组"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span> </span><span>rotate</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums</span><span>, </span><span>k</span><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>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>k</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>nums</span><span>.</span><span>unshift</span><span><span>(</span><span>nums</span><span>.</span></span><span>pop</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></section><section><h3>724 寻找数组的中心下标<a href="#724-寻找数组的中心下标"><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>pivotIndex</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>nums</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>let</span><span><span> </span><span>index</span><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>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>nums</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>leftSum</span><span> </span><span>=</span><span><span> </span><span>nums</span><span>.</span></span><span>slice</span><span>(</span><span>0</span><span><span>, </span><span>i</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><span>const</span><span> </span><span>rightSum</span><span> </span><span>=</span><span><span> </span><span>nums</span><span>.</span></span><span>slice</span><span><span>(</span><span>i</span><span> </span></span><span>+</span><span> </span><span>1</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>6</div></div><div><span>    </span><span>if</span><span><span> (</span><span>leftSum</span><span> </span></span><span>===</span><span><span> </span><span>rightSum</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>index</span><span> </span><span>=</span><span><span> </span><span>i</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span>      </span><span>break</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>return</span><span><span> </span><span>index</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></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/she-ji-mo-shi-chu-jian/</id>
      <title type="text">设计模式初见</title>
      <published>2022-10-31T21:49:41.000Z</published>
      <updated>2022-10-31T21:49:41.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/she-ji-mo-shi-chu-jian/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>什么是设计模式?为什么需要设计模式?<a href="#什么是设计模式为什么需要设计模式"><span>#</span></a></h2><p>为了解决软件设计中普遍存在的问题,前辈们总结了一些解决方案.而我们熟知的设计模式是前辈们通过经验总结下来的标准或者思路,它经受住了时间的考验;<code>设计模式</code>一词是由<code>埃里希·伽马</code>由建筑设计领域引进计算机世界;</p><p>那么设计模式体现在哪些地方呢?通常一个软件的组成由小到大&lt;编程语言&gt;(现在大部分语言都是面向对象的设计模式)+功能模块(多种设计模式+算法)+框架(很多种设计模式)+架构(服务器集群等)</p><p>使用设计模式能适应多变的需求,增强程序的拓展性,以更清晰的思路实现功能,以及很大程度的提高应用的可维护程度(可读性,规范化).如果你想成为一名合格的软件开发人员,那么设计模式是你必须接触的东西.</p></section>
<section><h2>设计原则(SOLID)<a href="#设计原则solid"><span>#</span></a></h2><p>我们先来看看unix的设计哲学吧,他和设计模式的一些设计原则有很多相似的部分</p><ul>
<li>小而美 保持程序的简洁,保证最简洁的api</li>
<li>每个程序只做一件事 每个函数就可以成为一个程序,这也是我们经常说的每个函数只做一件事</li>
<li>快速搭建原型 先做出来,再维护,再优化</li>
<li>可移植性大于性能 当可移植性和性能发生冲突时,优先取可移植性</li>
<li>可读性最重要 优先保证程序的可读性</li>
<li>提高复用性 这个可以说是程序员的共识了,解耦复用</li>
</ul><p>是不是总有种似曾相识的感觉?哈哈哈哈,是了,和我们平时开发所提倡的一些东西有很大交集.正式开始我们的五大设计原则吧</p><section><h3>S 单一职责<a href="#s-单一职责"><span>#</span></a></h3><p>一个程序只做好一件事;对于功能复杂的函数,可以进行拆分,每个部分保证独立;</p></section><section><h3>O 开放封闭<a href="#o-开放封闭"><span>#</span></a></h3><p>对拓展开放,对修改封闭;增加需求时,拓展新代码,而非修改已有代码</p></section><section><h3>L 李氏置换<a href="#l-李氏置换"><span>#</span></a></h3><p>子类能够覆盖父类;父类能出现的地方,子类就可以出现;</p></section><section><h3>I 接口独立<a href="#i-接口独立"><span>#</span></a></h3><p>保持接口单一独立,避免出现复合接口,和单一职责有点相似,但是专注于接口</p></section><section><h3>D 依赖导致<a href="#d-依赖导致"><span>#</span></a></h3><p>面向接口(抽象)编程,依赖于抽象而不依赖于具体;使用方只关注接口而不用去关注具体实现</p></section></section>
<section><h2>23种设计模式<a href="#23种设计模式"><span>#</span></a></h2><p>设计模式大体可分为以下几大类</p><section><h3>创建型<a href="#创建型"><span>#</span></a></h3><ul>
<li>工厂方法模式</li>
<li>抽象工厂模式</li>
<li>建造者模式</li>
<li>单例模式</li>
<li>原型模式</li>
</ul></section><section><h3>结构型<a href="#结构型"><span>#</span></a></h3><ul>
<li>适配器模式</li>
<li>装饰器模式</li>
<li>代理模式</li>
<li>外观模式</li>
<li>桥接模式</li>
<li>组合模式</li>
<li>享元模式</li>
</ul></section><section><h3>行为型<a href="#行为型"><span>#</span></a></h3><ul>
<li>策略模式</li>
<li>迭代器模式</li>
<li>模板方法模式</li>
<li>职责连模式</li>
<li>观察者模式</li>
<li>命令模式</li>
<li>备忘录模式</li>
<li>中介者模式</li>
<li>状态模式</li>
<li>解释器模式</li>
<li>访问者模式</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-di-san-zhang-yu-yan-ji-chu/</id>
      <title type="text">带你读红宝书-第三章-语言基础</title>
      <published>2022-10-09T16:53:43.000Z</published>
      <updated>2022-10-09T16:53:43.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-di-san-zhang-yu-yan-ji-chu/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>稍安勿躁<a href="#稍安勿躁"><span>#</span></a></h2><p>经过前边两个氛围轻松的章节,接下来就是直接接触JavaScript的本身了;想想还有点小刺激呢我会尽量以轻松的氛围把我读到的讲出来,避免你的枯燥.当然,也可以选择走马观花的过一遍啦</p></section>
<section><h2>语法<a href="#语法"><span>#</span></a></h2><p>对于经常写js的同学来说,可能觉得自己对js的语法早就了如指掌了,那么我们还是来看看有什么新的地方值得讨论的吧:</p><section><h3>区分大小写<a href="#区分大小写"><span>#</span></a></h3><p>js世界里的一切都需要区分大小写,无论是<code>变量</code>,<code>函数</code>,<code>属性</code>还是<code>操作符</code>都是需要评估大小写的,废话不多说,上代码更能激起共鸣</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>test</span><span>=</span><span>'1'</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>Test</span><span>=</span><span>'2'</span></div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>test</span><span>) </span></span><span>//1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这里的<code>test</code>和<code>Test</code>是不同的变量~</p></section><section><h3>标识符规则<a href="#标识符规则"><span>#</span></a></h3><p>标识符是指变量,函数,属性,函数参数的名称,它遵循下边的规则:</p><ul>
<li>第一个字符必须是字母,下划线,或者<code>$</code></li>
<li>剩下的字符可以是字母,下划线,或者<code>$</code></li>
</ul><blockquote><p>标识符中的字母可以是扩展 ASCII（Extended ASCII）中的字母，也可以是Unicode的字母字符，如 À 和 Æ（但不推荐使用）。</p></blockquote><p>上面这句话是红宝书第四版的原文.基于此,我还测试了使用我们的中文来定义变量,发现也是可以正常运行的.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>哈哈</span><span> </span><span>=</span><span> </span><span>123</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>哈哈</span><span>)</span></span><span>//123</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>哈哈,如果这样,我们能把保留的关键字都替换为中文,那…这,就是易语言!🤪</p><p>在我们的日常工作中,标识符的最佳实践为驼峰命名,形如<code>firstSecond</code>,<code>doSomethingImportant</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>// 单行注释</span></div></div><div><div><div>2</div></div><div><span>// console.log('hi')</span></div></div><div><div><div>3</div></div><div><span>// 多行注释</span></div></div><div><div><div>4</div></div><div><span>/* const run = () =&gt; {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console.log('run run run~');</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>es5</code>新增的一种运行模式,它可以将之前<code>es3</code>一些不规范的地方进行警告或者抛出错误,现在很多情况下,我们的工程都会默认开启这个模式来规避一些隐患</p><p>这个模式可以使用两种方式启用,<code>局部</code>和<code>全局</code>,下面展示一下用法</p><p>我们只需要在脚本顶部书写<code>"use strict";</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>"use strict"</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果我们希望严格模式只在某个局部作用域内发挥作用,那么我们可以在函数体内的顶部书写<code>"use strict";</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><span> </span><span>run</span><span>(){</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"use strict"</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>至于严格模式开启之后的一些限制,后续会介绍到的</p></section><section><h3>语句<a href="#语句"><span>#</span></a></h3><p>js中的语句通常是以’;‘结束的,在js的世界里其实是可以没有分号的,比如Vue作者尤雨溪,他就是习惯不加分号的,这在社区中一直是一个讨论的话题~</p><p>红宝书里是推荐加上分号的,原文:</p><blockquote><p>即使语句末尾的分号不是必需的，也应该加上。记着加分号有助于防止省略造成的问题，比如可以避免输入内容不完整。此外，加分号也便于开发者通过删除空行来压缩代码（如果没有结尾的分号，只删除空行，则会导致语法错误）。加分号也有助于在某些情况下提升性能，因为解析器会尝试在合适的位置补上分号以纠正语法错误。</p></blockquote><p>注意,这里的语句不等于表达式哈~</p></section><section><h3>关键字与保留字<a href="#关键字与保留字"><span>#</span></a></h3><p>关键字是指具有特殊用途的字符,比如流程控制的if,while等;ECMA-262的第六版关键字如下:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>break do in typeof</span></div></div><div><div><div>2</div></div><div><span>case else instanceof var</span></div></div><div><div><div>3</div></div><div><span>catch export new void</span></div></div><div><div><div>4</div></div><div><span>class extends return while</span></div></div><div><div><div>5</div></div><div><span>const finally super with</span></div></div><div><div><div>6</div></div><div><span>continue for switch yield</span></div></div><div><div><div>7</div></div><div><span>debugger function this</span></div></div><div><div><div>8</div></div><div><span>default if throw</span></div></div><div><div><div>9</div></div><div><span>delete import try</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>哪什么是保留字呢?保留字是未来关键字的预备军,和关键字一样不能作为标识符,这些保留字未来有可能会成为关键字,ECMA-262的第六版关键字如下:</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>enum</span></div></div><div><div><div>3</div></div><div><span>严格模式下保留:</span></div></div><div><div><div>4</div></div><div><span>implements package public</span></div></div><div><div><div>5</div></div><div><span>interface protected static</span></div></div><div><div><div>6</div></div><div><span>let private</span></div></div><div><div><div>7</div></div><div><span>模块代码中保留:</span></div></div><div><div><div>8</div></div><div><span>await</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这些词汇虽然不能用作标识符,但是却可以作为对象的属性名,但最好不要这样干,以确保你的代码能够兼容过去和未来的es版本.</p></section></section>
<section><h2>变量<a href="#变量"><span>#</span></a></h2><p>js是动态语言,这意味着你声明的变量可以用来保存任何类型的数据.红宝书里准确的把变量比作值的占位符.js发展到至今,我们有三种方式来声明我们的变量:<code>var</code>,<code>const</code>,<code>let</code>;而现在的js运行时几乎全部都支持了<code>let|const</code>,再加上前端工程化的推动,也可以很轻松实现向下兼容,综合来看<code>var</code>的方式可以成为过去式了,我们喜欢且应该使用<code>let</code>,<code>const</code>这两种方式来声明我们的变量.</p><section><h3>var<a href="#var"><span>#</span></a></h3><p>虽然这种方式已经成为过去式,但是了解它更有助于我们了解js这门语言本身.</p><p>通常我们可以这样声明一个变量:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>a</span><span>;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样一个变量<code>a</code>就声明好了,如果声明时没有赋值,那么你它的值是<code>undefined</code></p><p>由于动态语言的特性,我们可以这样用</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>a</span></span><span>=</span><span>1</span></div></div><div><div><div>2</div></div><div><span>a</span><span>=</span><span>"hello"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样是完全合乎js语言本身的,但是不推荐这样做,变量的最佳实践之一就是其保存的数据类型从始至终不发生改变.</p><section><h4>作用域<a href="#作用域"><span>#</span></a></h4><p>使用var时的最大诟病来了,来慢慢看吧</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>f</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>var</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>1</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>); </span></span><span>//a is not defined</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>var的作用域为函数作用域,函数执行完成退出时就会被销毁,很合理是吧,但是它存在一个变量提升的问题</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>f</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>a</span><span>); </span></span><span>//undefined</span></div></div><div><div><div>3</div></div><div><span>  </span><span>var</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>f</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当你执行这段代码时,会发现一切正常,这就是因为js的变量提升,它会将使用var声明的变量提升到函数顶部,它可以被转换为下面等价代码:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>f</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>var</span><span><span> </span><span>a</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><span>a</span><span>); </span></span><span>//undefined</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>a</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>f</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>var还存在一个可重复声明的bug?:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>a</span></span><span>=</span><span>1</span><span>;</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>a</span></span><span>=</span><span>"a"</span><span>;</span></div></div><div><div><div>3</div></div><div><span>var</span><span><span> </span><span>a</span></span><span>=!!</span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>) </span></span><span>//false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>wow~一切正常.正是由于var存在这一系列的问题,包括变量提升,可重复声明等一系列问题,所以我们急需要两位得力成员:<code>let</code>,<code>const</code></p></section></section><section><h3>let<a href="#let"><span>#</span></a></h3><p>let除了解决了上面var的问题,而且作用域变为了块级作用域!我们用一个典型的例子来说明它为我们带来的便利</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for</span><span> (</span><span>var</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>4</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setTimeout</span><span>(() </span><span>=&gt;</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>i</span><span>) </span></span><span>// 3,3,3,3</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></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在以前我们如果在一个for循环里延迟打印索引,你会发现索引打印出来永远时最大索引,这是因为我们异步打印的原因,setTimeout创建的宏任务会在我们主线代码执行完成之后再执行,等主线任务全部执行完延迟任务再去读取的时候,每次都是读取到的最大索引.那咋办?我们可以使用一个闭包来解决这个问题,我们把每次循环的索引保存进一个函数内,然后延迟函数引用函数内保存的变量,这样就可以解决这个问题了.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for</span><span> (</span><span>var</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>4</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>((</span><span>i</span><span>) </span><span>=&gt;</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></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>i</span><span>) </span></span><span>// 0,1,2,3</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></span><span>})(</span><span>i</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>但是如果你使用let来解决这个问题,那这个巴适了,改动成本几乎为0</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</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>4</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>setTimeout</span><span>(() </span><span>=&gt;</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>i</span><span>) </span></span><span>// 0,1,2,3</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></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对了,还没说明什么是块级,可以这么理解:<code>{}</code>花括号内的作用域就是块级作用域</p><p>wow~这也太爽了!这是因为啥呢?因为let是块级作用域,每次循环执行时都会重新创建一个块.等价代码贴出来你可能会明白一切</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</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>4</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// let i;这是被隐藏在for()的作用域</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></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>i</span><span>) </span></span><span>// 0,1,2,3</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></div></div><div><div><div>7</div></div><div><span>// {let i = 0}</span></div></div><div><div><div>8</div></div><div><span>// {let i = 1}</span></div></div><div><div><div>9</div></div><div><span>// {let i = 2}</span></div></div><div><div><div>10</div></div><div><span>// {let i = 3}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>暂时性死区问题<a href="#暂时性死区问题"><span>#</span></a></h4><p>另外关于变量提升这个问题,其实let也会存在变量提升,js引擎在解析代码时也会留意let,但是我们为什么无法提前访问它呢?那是因为在声明之前,会有一个<code>暂时性死区</code>.在<code>死区</code>存在区间(也就是被声明之前),我们无法使用任何方式来访问它.</p></section><section><h4>还有一个全局变量的问题<a href="#还有一个全局变量的问题"><span>#</span></a></h4><p>我们在全局作用域使用var声明变量时,变量会自动挂载到全局对象下作为一个属性,比如浏览器环境:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>1</span></div></div><div><div><div>2</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>window</span><span>.</span></span><span>a</span><span>); </span><span>// 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>换let,它并不会自动成为全局对象的一个属性</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>1</span></div></div><div><div><div>2</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>window</span><span>.</span></span><span>a</span><span>); </span><span>// undefined</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>条件声明和无法重新声明<a href="#条件声明和无法重新声明"><span>#</span></a></h4><p>条件声明是什么意思呢?指在符合某一条件时才声明这个变量,通常适用于全局环境中声明变量,由于目前模块化的盛行,这个东西可以抛弃,而且红宝书里也提了这是一种反模式,原文:</p><blockquote><p>注意 不能使用 let 进行条件式声明是件好事，因为条件声明是一种反模式，它让程序变得更难理解。如果你发现自己在使用这个模式，那一定有更好的替代方式。</p></blockquote><p>let是无法重新声明变量的,以下两种方式都是不被允许的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>1</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>a</span><span> </span></span><span>=</span><span> </span><span>1</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>b</span><span> </span></span><span>=</span><span> </span><span>1</span></div></div><div><div><div>5</div></div><div><span>var</span><span><span> </span><span>b</span><span> </span></span><span>=</span><span> </span><span>1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>const<a href="#const"><span>#</span></a></h3><p>由于大部分特性和let相同,我们这里只说一下区别吧.const声明的变量时必须赋予初值且后续无法重新赋值.看起来貌似const似乎声明后就无法改变,事实也是如此,但是如果你赋予的是一个引用类型,那么其內部的属性等是可以被改变的,而且也并没有范围const的原则.</p><p>那么如何区分let,const的使用场景呢?我的建议是能用const就用const,不行再用let.</p></section></section>
<section><h2>数据类型<a href="#数据类型"><span>#</span></a></h2><p>js有六种简单的数据类型,他们分别是:</p><ul>
<li>Undefined</li>
<li>Null</li>
<li>Boolean</li>
<li>Number(包含BigInt)</li>
<li>String</li>
<li>Symbol</li>
</ul><p>除此之外还有一种复杂类型,叫做<code>object</code>,它是一种无序的名值对的集合.严格意义上来讲的话,js里除了六种简单原始类型外,其余都可以归为<code>object</code>对象类型.</p><section><h3>typeof 检测数据类型的方式之一<a href="#typeof-检测数据类型的方式之一"><span>#</span></a></h3><p>由于js的数据类型是一个动态的,那就需要有一种可以判断当前数据类型的手段,typeof 操作符就是其中之一,使用方式如下:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>typeof</span><span><span> </span><span>variable</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>该操作符将返回以下类型:</p><ul>
<li>undefined 未定义</li>
<li>boolean 布尔值</li>
<li>string 字符串</li>
<li>number 数值</li>
<li>object 对象</li>
<li>function 函数</li>
<li>symbol 符号</li>
</ul><p>特别说明&lt;你可能也发现了&gt;,返回值里为什么没有null呢?这是来自于js的一个远古bug.红宝书里也有解释到(但这其实是一个委婉的说法😀)&lt;这是因为特殊值&gt; null 被认为是一个对空对象的引用。</p></section><section><h3>Undefined<a href="#undefined"><span>#</span></a></h3><p>这个类型是一个特殊意义的值,只有一个值;如果你声明了一个变量,但是并没有赋值给它,那么它的值就是undefined.</p><p>书中还说到一种情况,就是如果你希望你的比那辆显式的声明一个undefined变量,使用形如下边的方式是不推荐的</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>undefined</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>a</span></span><span><span>=</span><span>void</span></span><span> </span><span>0</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Null<a href="#null"><span>#</span></a></h3><p>同样是一个特殊类型的值,也只有一个值.null可以用来表示一个空指针对象,所以如果你对它使用typeof操作符,得到的结果会是一个object类型.(其实是一个bug 0.0)</p><p>如果你确信将来这个变量会是一个对象类型的值时,你就可以将你的变量初始化为一个null值.</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>null</span><span>;</span></div></div><div><div><div>2</div></div><div><span>a</span><span>=</span><span>{}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Boolean<a href="#boolean"><span>#</span></a></h3><p>这个没啥好说的吧,大家使用都很频繁.有true和false两个值.</p></section><section><h3>Number<a href="#number"><span>#</span></a></h3><blockquote><p>ECMAScript 中最有意思的数据类型或许就是 Number 了。</p></blockquote><p>红宝书开篇就说了这句话,换句话说就是number类型可能是js里最古怪行为的类型了,哈哈哈</p><p>严格意义来说js中是不分浮点和整数类型的</p><section><h4>浮点<a href="#浮点"><span>#</span></a></h4><p>要定义浮点类型,只需要数字中包含小数点就行</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>1.1</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>b</span></span><span>=</span><span>0.1</span></div></div><div><div><div>3</div></div><div><span>let</span><span><span> </span><span>c</span></span><span>=</span><span>.1</span><span> </span><span>// 不推荐,应该添0 &gt; 0.1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你只需要定义一个整数,那么就不要使用浮点类型,因为浮点类型占用的内存空间是是整数类型的两倍,js引擎一般来说也会尽量帮你把浮点类型抓换为整数类型,形如1.0这种会被直接转换为1的整数类型.对于非常大或非常小的数值,浮点数可以使用科学计数法来表示(貌似在我的职业生涯中还没有相关的实践 =.=).</p><p>浮点值得精确度最高可达17位小数,由于占用双倍内存,这里可以倒推整数类型的范围,应该同样是是17位.</p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-di-er-zhang-html-zhong-de-javascript/</id>
      <title type="text">带你读红宝书-第二章-HTML中的JavaScript</title>
      <published>2022-10-07T08:07:48.000Z</published>
      <updated>2022-10-07T08:07:48.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-di-er-zhang-html-zhong-de-javascript/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>如何将JavaScript插入到页面中?<a href="#如何将javascript插入到页面中"><span>#</span></a></h2><p>我们通常使用<code>&lt;script&gt;</code>元素来将脚本插入到页面中,不管是行内还是外部的脚本,他都会阻塞页面的渲染,你可以通过一些属性来改善,它的一些属性(?&lt;表示可选属性&gt;):</p><ul>
<li>async?&lt;只对外部脚本有效&gt;,立即下载这个脚本,但不能阻止其他资源和脚本的加载</li>
<li>charset?&lt;字符集编码&gt;,发展到现在可以直接忽略它的存在了</li>
<li>crossorigin?&lt;配置请求的cors设置&gt;,。crossorigin=“anonymous”配置文件请求不必设置凭据标志。crossorigin=“use-credentials”设置凭据标志，意味着出站请求会包含凭据。</li>
<li>defer?&lt;只对外部脚本有效&gt;,表示脚本延迟到文档被完全解析和显示之后再执行.</li>
<li>integrity?&lt;可以使用指定的加密签名来验证资源的完整性&gt;.比如放置到CDN上的资源被恶意修改,那么它的签名不会匹配,页面将会抛出错误.</li>
<li>language?&lt;用来执行脚本的版本&gt;,如今已经废弃</li>
<li>src?&lt;外部脚本的路径&gt;</li>
<li>type?&lt;表示脚本的类型&gt;,按照惯例，这个值始终都是”text/javascript”.如果这个至是<code>module</code>,则代表这个脚本是ES6模块,将可以再这个元素内使用<code>import</code>,<code>export</code>等关键字</li>
</ul><section><h3>行内jio本<a href="#行内jio本"><span>#</span></a></h3><p>来看一个有趣的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span> </span><span>function</span><span> </span><span>sayScript</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>"&lt;/script&gt;"</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>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这段代码看似没什么问题,实际上他会报错,因为脚本被提前结束了,如果想要正常输出<code>"&lt;/script&gt;"</code>,就需要对其进行转义啦</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span> </span><span>function</span><span> </span><span>sayScript</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>"&lt;</span><span>\/</span><span>script&gt;"</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>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>外部jio本<a href="#外部jio本"><span>#</span></a></h3><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>"example.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用了src属性后,不应该再使用行内样式的书写,通常行内的代码不会再次执行</p><p>❌bad example:</p><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>"example.js"</span><span>&gt;</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>123456</span><span>)</span></div></div><div><div><div>3</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>src可以是一个网络请求,也可以是同域中的资源,当是一个url时,会发出一个get请求,不受同源策略的影响,但是其内执行的脚本受同源策略的影响</p><section><h4>defer属性&lt;讨论下脚本在页面中的位置&gt;<a href="#defer属性"><span>#</span></a></h4><p>我们通常会将JavaScript脚本放置到页面底部,这是什么原因呢?我们前文提到过,脚本的执行还会阻塞页面的渲染,所以可以想象到,如果我们一个脚本所执行需要的耗时很长,而它被我们放置到了头部,那么再这个脚本执行完之前,一切渲染功能都停止了,用户看到的会是一个白页面,而将脚本执行完毕,渲染完毕,这个白页面的时间太长了,会让用户有一种感觉,那就是你的网站加载很慢~而放置到底部,就不会有这个问题了,即使用户提前看到的不是一个完整的应用,但是体验仍旧会好很多</p><p>如果你硬要将脚本放置到头部,那么也会有解决方案,那就是defer,他会等文档全部解析显示之后才会开始执行,但是他只对外部脚本有效,行内脚本会忽略这个属性.如果多个defer脚本,他们会按照放置的顺序执行.</p></section><section><h4>async属性<a href="#async属性"><span>#</span></a></h4><p>它和defer基本一致,不同点:</p><ul>
<li>它可能不会按照放置的顺序执行,所以你必须保证他们之间没有依赖关系</li>
<li>不阻塞浏览器的其他工作,即不用等待该脚本的下载和执行,也因此不应该出现操作dom的情况</li>
</ul><p>你可以理解为这个属性专门开启了一条线程来负责该脚本的下载执行,完全不影响主线程的执行</p></section></section></section>
<section><h2>动态脚本<a href="#动态脚本"><span>#</span></a></h2><p>我们可以使用DOM的<code>createElement</code>这个api来创建一个动态插入到页面的脚本</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>script</span><span> </span></span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'script'</span><span>);</span></div></div><div><div><div>2</div></div><div><span><span>script</span><span>.</span></span><span>src</span><span> </span><span>=</span><span> </span><span>'gibberish.js'</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>script</span><span>.</span></span><span>async</span><span> </span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>document</span><span>.</span></span><span>head</span><span>.</span><span>appendChild</span><span><span>(</span><span>script</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在将创建的元素插入到页面之前,是不会发出请求的,这种方式会默认以异步的方式加载脚本,所以我们手动设置了<code>async</code>属性为false;这种方式对浏览器的预加载是不可见的,所以性能会严重影响,所以我们可以在头部添加</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>link</span><span> </span><span>rel</span><span>=</span><span>"preload"</span><span> </span><span>href</span><span>=</span><span>"gibberish.js"</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>来告知浏览器将来我们会使用这些资源,请进行预加载</p></section>
<section><h2><code>&lt;noscript&gt;</code>元素<a href="#noscript元素"><span>#</span></a></h2><p>用于在不支持的浏览器中,其中的内容会被当做文档解析;这点经常搞<code>canvas</code>的人会比较有种熟悉感:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>canvas</span><span>&gt;你的浏览器不支持canvas&lt;/</span><span>canvas</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>这个章节讨论了如何将JavaScript脚本插入到页面中,并介绍了他们之间的差异</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-di-yi-zhang-shen-me-shi-javascript/</id>
      <title type="text">带你读红宝书-第一章-什么是JavaScript</title>
      <published>2022-10-07T07:14:05.000Z</published>
      <updated>2022-10-07T07:14:05.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/dai-ni-du-hong-bao-shu-di-yi-zhang-shen-me-shi-javascript/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么会出现JavaScript?<a href="#为什么会出现javascript"><span>#</span></a></h2><p>想象一下你身处拨号上号的年代,JavaScript还没诞生.你打开了一个站点,看着网页里的一大堆表单,你满心欢喜的填满了表单,然后点了提交.然后等了许久后,网页提示你的填写不规范.你虽然抱怨了几句,还是着手重新填写起来,一切填写完毕,再次点了提交,许久后,页面再次提示你填写不规范.”fuck,👴不用了!”你的耐心被这一大片表单以及漫长等待消磨殆尽,最终放弃了使用这个站点.这个站点因为用户体验不佳失去了一名用户,还有其他千千万万的用户.</p><p>为了解决这个问题,一个脚本语言出现了,它让你一定程度不用去和服务器交互即可校验你的填写,wow~渐渐的,这种可以在客户端校验表单的脚本语言成为了浏览器的标配.而随着web端日益流行,对脚本语言的需求不再是简单的校验.这个时候网景公司开发出了JavaScript,这个命名是为了蹭当时火热的java的热度.就这样,JavaScript诞生啦!</p></section>
<section><h2>ECMAScript标准<a href="#ecmascript标准"><span>#</span></a></h2><p>眼看着JavaScript越来越火,而当时借着捆绑式销售的IE巨头-微软无法眼睁睁看着对手强大,后续微软发布了自己的JScript.业界开始担心没有一个统一的标准,担心两个版本的差异导致web端的分裂.于是ECMAScript诞生了!各家浏览器需要遵循这个标准来实现自己的JavaScript脚本.</p><p>通常,浏览器厂商除了实现ECMAScript标准,还会去实现DOM,BOM;所以通常浏览器端而言,一个完整的JavaScript实现分为三个部分:</p><ul>
<li>ECMAScript核心</li>
<li>DOM文档对象模型</li>
<li>BOM浏览器对象模型</li>
</ul><p>虽然DOM,BOM没有像ECMAScript这样一个标准,不过他们也有标准可遵循,BOM在html5出现之后得到了一定的覆盖,而DOM择优W3C维护DOM标准来覆盖</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>这一章介绍了JavaScript出生的原因以及后续相关标准的发展;虽然发展中途出现很多问题,不过现在的web开发者已经可以使用大部分公共特性来开发页面了,不用太过于去区分浏览器差异.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/zou-jin-qian-duan-gong-cheng-hua-webpackloader-de-ru-men-ji-zhi-shi/</id>
      <title type="text">走近前端工程化-WebPackloader的入门级知识</title>
      <published>2022-09-26T11:30:49.000Z</published>
      <updated>2022-09-26T11:30:49.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/zou-jin-qian-duan-gong-cheng-hua-webpackloader-de-ru-men-ji-zhi-shi/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>什么是loader?<a href="#什么是loader"><span>#</span></a></h2><p>webpack原生只支持js,json等数据的解析,如果想要他支持其他资源的解析,那么就需要loader上场了,这也是loader的主要作用,它将资源解析成webpack能识别的形状~</p><p>通常遇到需要的loader,我们都会区社区直接找一些loader配置上去,但是你不好奇他们是怎么工作的吗?来手写一些loader,感受下吧~</p></section>
<section><h2>预先知识-分类以及优先级<a href="#预先知识-分类以及优先级"><span>#</span></a></h2><p>按照流程分类,loader可分为下面四个类型:</p><ul>
<li>pre： 前置 loader</li>
<li>normal： 普通 loader</li>
<li>inline： 内联 loader</li>
<li>post： 后置 loader</li>
</ul><p>其执行的优先级:<code>pre &gt; normal &gt; inline &gt; post</code>,同等级的 loader 执行顺序为：<code>从右到左，从下到上</code>,可通过<code>enforce</code>来配置该loader属于哪种类型:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>rules</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>enforce</span><span>:</span><span> </span><span>"pre"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>      </span><span>test</span><span>:</span><span><span> /</span><span>\.</span><span>js</span></span><span>$</span><span>/</span><span>,</span></div></div><div><div><div>5</div></div><div><span>      </span><span>loader</span><span>:</span><span> </span><span>"loader1"</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><section><h3>inline loader的前缀<a href="#inline-loader的前缀"><span>#</span></a></h3><ul>
<li><code>!</code> 跳过 normal loader。</li>
<li><code>-!</code> 跳过 pre 和 normal loader。</li>
<li><code>!!</code> 跳过 pre、 normal 和 post loader。</li>
</ul><p>示例:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>Styles</span><span> </span><span>from</span><span> </span><span>'-!style-loader!css-loader?modules!./styles.css'</span><span>;</span><span>`</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>pitch loader的执行流程<a href="#pitch-loader的执行流程"><span>#</span></a></h2><p>pitch是loader上的一个方法,可以更方便我们进行流程控制;如果pitch有返回值,那么将触发熔断机制;如果触发熔断机制,那么这个pitch执行完成之后,流程会返回到上一个loader的主函数,这样更利于在代码中来控制流程,而不是通过固定的配置</p><p>流程可以看下图
<img src="./imgs/20220924104436.png" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>source</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>'normal excution'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span><span> </span><span>source</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>// loader上的pitch方法，非必须</span></div></div><div><div><div>6</div></div><div><span>module</span><span>.</span><span>exports</span><span>.</span><span>pitch</span><span> </span><span>=</span><span>  </span><span>function</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>'pitching graph'</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>// todo</span></div></div><div><div><div>9</div></div><div><span>    </span><span>return</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><h2>异步loader<a href="#异步loader"><span>#</span></a></h2><p>异步loader通过调用<code>async</code>方法返回一个回调,通过回调完成异步操作,后续loader会被该loader阻塞</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>source</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>let</span><span><span> </span><span>cb</span><span> </span></span><span>=</span><span> </span><span>this</span><span>.</span><span>async</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>// 在异步回调中手动调用 cb 返回处理结果;第一个参数为错误信息</span></div></div><div><div><div>5</div></div><div><span>      </span><span>cb</span><span>(</span><span>null</span><span><span>, </span><span>source</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}, </span><span>3000</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>raw-loader<a href="#raw-loader"><span>#</span></a></h2><p>其实与其他loader没有什么差异,只因为它的内容是Buffer流,特别适合处理静态资源,比如图片,视频等,需要通过raw属性来告知webpack,该loader进行接受的是二进制数据</p><div><figure><figcaption><span>loaders/simple-raw-loader.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>source</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 将输出 buffer 类型的二进制数据</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>source</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span><span> </span><span>source</span></span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>// 告诉 wepack 这个 loader 需要接收的是二进制格式的数据</span></div></div><div><div><div>7</div></div><div><span>module</span><span>.</span><span>exports</span><span>.</span><span>raw</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>接下来,我们自己开发一个loader<a href="#接下来我们自己开发一个loader"><span>#</span></a></h2><p>我们来为每一个js文件前边加上一句<code>console.log('hello')</code></p><p>那么代码下面这样写,这个loader就完成了</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><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>content</span><span> 文件内容</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>map</span><span> sourcemap</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>meta</span><span> 别的loader传递的参数</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>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span> </span><span>loaderHello</span><span><span>(</span><span>content</span><span>, </span><span>map</span><span>, </span><span>meta</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><span>content</span><span>, </span><span>map</span><span>, </span><span>meta</span><span>]);</span></span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>pre</span><span> </span><span>=</span><span> </span><span>`console.log('hello')</span><span>\n</span><span>`</span><span>;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>return</span><span><span> </span><span>pre</span><span> </span></span><span>+</span><span><span> </span><span>content</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>配置好就可以愉快打包啦~</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>rules</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>test</span><span>:</span><span><span> /</span><span>\.</span><span>js</span></span><span>$</span><span>/</span><span>i</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>loader</span><span>:</span><span> </span><span>resolve</span><span><span>(</span><span>__dirname</span><span>, </span></span><span>"../loader/hello/index.js"</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>我们这个前缀是硬编码,我们希望这部分内容是动态可选的,所以我们需要配置我们的loader,那我们需要<code>getOptions</code>这个api来帮助我们;开搞~</p><p>配置格式需要指定一个<code>JsonSchema</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>"type"</span><span>: </span><span>"object"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"properties"</span><span>: {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"code"</span><span>: {</span></div></div><div><div><div>5</div></div><div><span>      </span><span>"description"</span><span>: </span><span>"pre code content,allow null"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>      </span><span>"type"</span><span>: </span><span>"string"</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>"additionalProperties"</span><span>: </span><span>false</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>接下来对loader进行改造</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><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>content</span><span> 文件内容</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>map</span><span> sourcemap</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>meta</span><span> 别的loader传递的参数</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>const</span><span> </span><span>schema</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>"./schema.json"</span><span>);</span></div></div><div><div><div>7</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span> </span><span>loaderHello</span><span><span>(</span><span>content</span><span>, </span><span>map</span><span>, </span><span>meta</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>option</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>getOptions</span><span><span>(</span><span>schema</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>content</span><span>, </span><span>map</span><span>, </span><span>meta</span><span>, </span><span>option</span><span> });</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>const</span><span> </span><span>pre</span><span> </span><span>=</span><span> </span><span>`</span><span>${</span><span>option</span><span>.</span><span>code</span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>`</span><span>;</span></div></div><div><div><div>12</div></div><div><span>  </span><span>return</span><span><span> </span><span>pre</span><span> </span></span><span>+</span><span><span> </span><span>content</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>配置项</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>rules</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>test</span><span>:</span><span><span> /</span><span>\.</span><span>js</span></span><span>$</span><span>/</span><span>i</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>loader</span><span>:</span><span> </span><span>resolve</span><span><span>(</span><span>__dirname</span><span>, </span></span><span>"../loader/hello/index.js"</span><span>),</span></div></div><div><div><div>5</div></div><div><span>    </span><span>options</span><span>:</span><span> {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>code</span><span>:</span><span> </span><span>"console.log('msg from config')"</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></code></pre><div><div></div><div></div></div></figure></div><p>这样,一个<code>hello world</code>级别的loader就完成啦~</p></section>
<section><h2>其它loader示例<a href="#其它loader示例"><span>#</span></a></h2><p>来手动创建一些loader感受下</p><section><h3>babel-loader<a href="#babel-loader"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>babel</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>"@babel/core"</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>schema</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>"./schema.json"</span><span>);</span></div></div><div><div><div>3</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>content</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>call</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>async</span><span>();</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>options</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>getOptions</span><span><span>(</span><span>schema</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>babel</span><span>.</span><span>transform</span><span><span>(</span><span>content</span><span>, </span><span>options</span><span>, </span></span><span>function</span><span><span> (</span><span>err</span><span>, </span><span>result</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span><span> (</span><span>err</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>      </span><span>call</span><span><span>(</span><span>err</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>10</div></div><div><span>      </span><span>call</span><span>(</span><span>null</span><span><span>, </span><span>result</span><span>.</span></span><span>code</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><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>file-loader<a href="#file-loader"><span>#</span></a></h3><p>这里用到的<code>loader-utils</code>可以自行去看一下:<a href="https://github.com/webpack/loader-utils" target="_blank">https://github.com/webpack/loader-utils</a></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>loaderUtils</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>"loader-utils"</span><span>);</span></div></div><div><div><div>2</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>content</span><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>const</span><span> </span><span>filename</span><span> </span><span>=</span><span><span> </span><span>loaderUtils</span><span>.</span></span><span>interpolateName</span><span>(</span><span>this</span><span>, </span><span>"[hash].[ext]"</span><span>, {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>content</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></div></div><div><div><div>8</div></div><div><span>  </span><span>this</span><span>.</span><span>emitFile</span><span><span>(</span><span>filename</span><span>, </span><span>content</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>  </span><span>// 暴露出去,众所周知webpack眼里所有资源都是模块</span></div></div><div><div><div>10</div></div><div><span>  </span><span>return</span><span> </span><span>`export default '</span><span>${</span><span>filename</span><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>配置:</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><span>test</span><span>:</span><span><span> /</span><span>\.</span><span>(png</span><span>|</span><span>jpe</span><span>?</span><span>g</span><span>|</span><span>gif)</span></span><span>$</span><span>/</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>loader</span><span>: </span><span>"./loaders/file-loader.js"</span><span>,</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>type</span><span>: </span><span>"javascript/auto"</span><span>, </span><span>// 解决图片重复打包问题,否则webpack会自己再去打包一次(如果不配置,你的输出目录将会出现双倍资源)</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>
<section><h2>结语<a href="#结语"><span>#</span></a></h2><p>就使用上来说,还是挺简单的,但是这种东西挺注重实战的,只有在实战中才能真正有更深入的理解.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/yi-wen-che-di-gao-dong-http/</id>
      <title type="text">一文彻底搞懂http</title>
      <published>2022-09-15T03:04:06.000Z</published>
      <updated>2022-09-15T03:04:06.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/yi-wen-che-di-gao-dong-http/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>前文<a href="#前文"><span>#</span></a></h2><p><code>http</code>,我们熟悉又陌生的名词;熟悉是因为我们天天都有在使用它,又因很多前端同学工作几年,甚至以为http请求只有<code>get</code>和<code>post</code>两种请求的方法,足以证明<code>http</code>的陌生程度.我以前对<code>http</code>也是没有引起重视的,只是有简单的了解一些,直到最近几次面试都问到这个😭,我才意识到这方面知识的欠缺.</p><p>没关系,有心开始就不算晚,今天就让我们彻底搞懂这个玩意,去和面试官谈笑风声🤔.</p></section>
<section><h2>先来个经典的面试题吧,从键入<code>url</code>到<code>视图呈现</code>发生了什么?<a href="#先来个经典的面试题吧从键入url到视图呈现发生了什么"><span>#</span></a></h2><p>不少面试官一上来就问这玩意😀</p><p>我们输入<code>baidu.com</code>回车,浏览器会首先帮我们补全url变成<code>https://www.baidu.com/</code>,然后检查本地缓存,如果资源需要从服务器获取,那么就需要域名解析了,这个时候就会去最近(最近是说,如果你的本机或者路由器有对应的映射关系,那么就不需要去网络查询,最简单的情况是你直接改了hosts文件)的<code>dns服务器</code>获取<code>url</code>对应的ip地址,好,找到你了!开始交♂流吧!交流就需要桥梁啊,这个时候就该<code>tcp链接</code>出马了,只见客户端和服务器之间架起了一座桥,是的他们建立了<code>tcp链接</code>,建立结束后,客户端就可以发送请求啦,,服务器处理完对应请求然后返回给客户端,好的交流结束,关闭<code>tcp链接</code>.客户端收到这些资源就该干活了,我们的客户端是浏览器,浏览器开始解析这些资源,通过html解析成dom树,通过css解析为css规则树,最后dom树和css规则树合并为渲染树,js的执行和加载都会影响上边两者的解析和渲染(这也是为啥我们一般放到底部的原因),浏览器会根据这颗渲染树计算每个节点的位置大小等信息,然后在页面绘制出这棵树,视图就呈现啦~</p></section>
<section><h2>http正式登场<a href="#http正式登场"><span>#</span></a></h2><p>可以看到上边我们交换数据的地方是一笔带过的🤫,其实中间有很多小细节,让我为你一一道来</p><section><h3>首先我们要说的是网络分层模型<a href="#首先我们要说的是网络分层模型"><span>#</span></a></h3><p>经典的分层模型(括号内是代表事物):</p><ul>
<li>物理层(网线,光缆,网卡硬件…)</li>
<li>数据链路层(网卡软体(驱动)…)</li>
<li>网络层(路由器…)</li>
<li>传输层(tcp,udp…)</li>
<li>应用层(http,samba,ftp…)</li>
</ul><p>我们的网络是很多东西组成的,举个例子,如果你的机器没有网卡,你能上网吗?很明显不能,因为它也属于网络模型中不可或缺的部分,网卡在这里担任的就是数据链路层(其实也可以摆在物理层)</p><p>我们不需要关注低三层&lt;物理层&gt;,数据链路层,网络层;因为传输层会屏蔽掉下层的通信细节,嘿嘿嘿,正是我们想要的形状;我们的http是建立在tcp之上的,而且也屏蔽了传输层的细节,嘿嘿嘿</p><p>他们之间是有一个封包和拆包的关系的,这头包装那头拆最终每层都能见到属于自己的东西,可以见下图:
<img src="./imgs/经典五层网络模型.png" /></p></section><section><h3>http版本发展历史<a href="#http版本发展历史"><span>#</span></a></h3><p>来观望一下http发展历史</p><section><h4>0.9<a href="#09"><span>#</span></a></h4><p>初版是很简陋的</p><ul>
<li>他只有一个请求方式<code>get</code></li>
<li>没有数据的描述信息(header)</li>
<li>服务器发送完毕,直接关闭<code>tcp链接</code>,无法实现链接的复用</li>
</ul></section><section><h4>1.0<a href="#10"><span>#</span></a></h4><ul>
<li>增加了新的请求方式(<code>post</code>,<code>put</code>…)</li>
<li>增加了状态码(status code)和数据描述信息(header)</li>
<li>增加了多字符集支持,缓存等</li>
</ul></section><section><h4>1.1<a href="#11"><span>#</span></a></h4><ul>
<li>持久化链接,实现链接的复用,不用频繁创建链接(服务端可以通过<code>Connection</code>头来控制)</li>
<li>pipeline</li>
<li>增加host和一些其他命令</li>
</ul></section><section><h4>2.0(未来的主流)<a href="#20未来的主流"><span>#</span></a></h4><ul>
<li>数据都以二进制传输,在此之前数据都是字符串形式</li>
<li>对pipeline的优化,同一链接里的多个请求不再需要按照顺序</li>
<li>头部信息的压缩以及服务端推送(server push)等</li>
</ul><p>http2的诞生解决了长链接必须按照顺序发送请求的问题,以帧为单位发送数据可以不论先后,服务器收到后会根据帧信息合成完整的数据.这里值得提一下服务端推送,以往我们在页面里的css和js等需要通过解析html之后才能发起请求的资源,可以通过服务端推送直接推送给客户端,这些资源不再需要等待html的解析之后发起的请求,相当于和html是并行加载的,哇,加载速度又提高了一丢丢</p></section></section><section><h3>来了老弟!三次握手<a href="#来了老弟三次握手"><span>#</span></a></h3><p>经典的不灭之握😱~</p><ul>
<li>第一握&lt;客户端发送&gt; <code>SYN=1,Seq=X</code></li>
<li>第二握&lt;服务端收到后&gt;,发送 <code>SYN=1,ACK=x+1,Seq=Y</code></li>
<li>第三握&lt;客户端收到确认&gt;(ACK===X+1)后,发送 <code>ACK=Y+1,Seq=Z</code>,服务端确认(ACK===Y+1)后就开始等数据</li>
</ul><p>说一下为什么需要三次握手吧,它主要解决一件事情&lt;就是为了防止错误的数据包被发送到服务端&gt;,产生错误</p><p>流程图如下:
<img src="./imgs/tcp三次握手.png" /></p></section><section><h3>报文格式<a href="#报文格式"><span>#</span></a></h3><p>每个报文第一行都被称为起始行(起始行各部分用空格隔开),然后从第二行开始就属于头部,最下边是主体部分,主体和头部之间会空起一行分割</p><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>GET /baidu.com HTTP/1.1</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>Accept:text/*</span></div></div><div><div><div>6</div></div><div><span>一一一一一一一一一一一一一一一一一一一一</span></div></div><div><div><div>7</div></div><div><span>请求主体(get post其实都可以有请求体,千万别以为get没有请求体)</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></div></div><div><div><div>2</div></div><div><span>HTTP/1.1 200 OK</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>Content-type:text/plain</span></div></div><div><div><div>6</div></div><div><span>一一一一一一一一一一一一一一一一一一一一</span></div></div><div><div><div>7</div></div><div><span>响应主体</span></div></div><div><div><div>8</div></div><div><span>HI,我是小度小度</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>这里稍微说一下状态码各区间的含义<a href="#这里稍微说一下状态码各区间的含义"><span>#</span></a></h4>

<table><thead><tr><th>区间</th><th>含义</th></tr></thead><tbody><tr><td>100~199</td><td>还需要做一些额外的操作,这个请求才会返回给你</td></tr><tr><td>200~299</td><td>这个请求能处理,并能成功返回</td></tr><tr><td>300~399</td><td>重定向</td></tr><tr><td>400~499</td><td>发送的请求有问题</td></tr><tr><td>500~599</td><td>服务器端出现问题</td></tr></tbody></table><p>这里说一下<code>301</code>/<code>302</code>状态码,服务端只需要在头部生命”Location”,就会去往声明的地址,这两者缺一不可;<code>301</code>是指永久重定向,下次浏览器就不用问服务器了直接重定向,所以要慎重使用,因为初次请求之后控制权就不在服务端了,<code>302</code>是临时的,每次都要和服务器交互获取跳转地址</p></section></section><section><h3>浏览器的同源策略<a href="#浏览器的同源策略"><span>#</span></a></h3><p>为什么会存在跨域呢?一句话,为了防止恶意网页可以获取其他网站的本地数据。</p><p>同源策略的影响:</p><ul>
<li>不能获取dom结构</li>
<li>不能获取cookie/indexdb/Storage</li>
<li>不能正常获取非同源ajax响应数据</li>
</ul><p>注意,这是浏览器的特色行为,其实在跨域ajax整个发生过程中,请求达到了服务端,服务端也返回了数据,但是浏览器意识到这是跨域行为,然后给拦截了.</p><p>怎么解决ajax跨域呢?目前主流的方式:</p><ul>
<li>JSONP(其实非主流)</li>
<li>proxy代理服务器</li>
<li>设置响应头的跨域范围,“Access-Control-Allow-Origin”: ”*”</li>
</ul><section><h4>CORS预请求(也叫预跨域请求,来自某次面试的丢人痛击)<a href="#cors预请求也叫预跨域请求来自某次面试的丢人痛击"><span>#</span></a></h4><p>就上面跨域范围设置后,还值得一说的是,其实也并非所有请求方式和请求头可以正常放行,其实这样设置之后也只是<code>get</code>,<code>post</code>,<code>head</code>请求方法可以通行,也是浏览器默认允许的跨域请求方式,其余方法是不允许跨域的,同样的情况请求头也有一些限制;怎么解决这种情况呢,那就是cors预请求,浏览器会先向服务器发起预请求来查验哪些头部和方法可以跨域,然后才会发送跨域请求;这个操作需要服务器端配置好相应的允许名单(Access-Control-Allow-Methods以及Access-Control-Allow-Headrs);这个问题也让我想起了某一次面试,面试官问我,有没有遇到过浏览器发请求的时候会发送两次请求,第一次是空的(你会看到请求的方法是),第二次才会真正发起请求,我心想这是啥玩意?还有这种情况?果然,无知最可怕啊😂</p><p>与上边配套的还有一个<code>Access-Control-Max-Age</code>,使用它可以让浏览器在预请求之后,在一定的时间内不需要再次发起预请求,可以直接发起跨域请求</p></section></section><section><h3>http缓存(Cache-Control)<a href="#http缓存cache-control"><span>#</span></a></h3><p>又是一个经常问到的点,我们可以利用它来减少请求数,提高性能,先来看一些它的主要概念</p><section><h4>什么是可缓存性?<a href="#什么是可缓存性"><span>#</span></a></h4><p>可缓存性是指可以在网络哪一个环节进行该资源的缓存,通常在header里指明就行,但是headerd通常只是一种声明,不是强制性的,需要客户端去遵守这些规则,这个一定要注意</p><ul>
<li>public 可以在任何节点进行缓存</li>
<li>private 只可以在发起请求的客户端上进行缓存</li>
<li>no-cache 可以缓存,但是必须和服务器验证是否可以使用缓存</li>
<li>no-store 任何节点都不能进行缓存,永远进行网络请求</li>
<li>no-transform 禁止节点对资源进行更改,比如压缩等</li>
</ul></section><section><h4>什么是缓存期限?<a href="#什么是缓存期限"><span>#</span></a></h4><p>是指缓存的有效期,超过这个有效期,缓存会被标记为失效,再次请求需要从网络上获取</p><ul>
<li>max-age</li>
<li>s-max-age 只有在代理服务器生效,比max-age优先级高</li>
<li>max-stale 这个期限内不受缓存有效期的影响(浏览器使用不到)</li>
</ul></section><section><h4>验证规则(协商缓存)<a href="#验证规则协商缓存"><span>#</span></a></h4><ul>
<li>must-revalidate 缓存到期后,必须进行验证才能使用</li>
<li>proxy-revalidate 同上,但是用在缓存服务器</li>
</ul><p>这里简单说一下验证的方式,通常有两种&lt;一种是对比修改的时间&gt;(last-modified),一种是对比文件的签名(内容的更改,etag)</p></section><section><h4>所谓的强缓存和协商缓存<a href="#所谓的强缓存和协商缓存"><span>#</span></a></h4><p>强缓存其实就是指定缓存期限来实现,协商缓存就是采用验证规则来实现,如果两者都用上了,那么强缓存是优先级更高的</p></section></section><section><h3>cookie以及session<a href="#cookie以及session"><span>#</span></a></h3><p>服务端响应头部设置<code>Set-Cookie</code>后,这玩意就会出现在请求头中</p><p>它的一些属性:</p><ul>
<li>max-age 设置失效时间,经过多少时间失效</li>
<li>expires 设置失效时间,到某个时间点失效</li>
<li>Secure 表示只有在请求时https时,cookie才会被发送</li>
<li>HttpOnly 只能通过服务端读取操作,客户端是无法读取的</li>
<li>domain 使用范围</li>
</ul><p>session其实并不依赖于cookie,它只是服务端用来确定空间的一种依据,不过通常在web中,多用cookie来实现</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>请求端</span></div></div><div><div><div>2</div></div><div><span>- Accept 类型</span></div></div><div><div><div>3</div></div><div><span>- Accept-Encoding 编码,压缩(gzip)</span></div></div><div><div><div>4</div></div><div><span>- Accept-Language 语言</span></div></div><div><div><div>5</div></div><div><span>- User-Agent UA客户端标识</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>- Content-Type 类型</span></div></div><div><div><div>9</div></div><div><span>- Content-Encoding 编码,压缩(gzip)</span></div></div><div><div><div>10</div></div><div><span>- Content-Language 语言</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>https<a href="#https"><span>#</span></a></h2><p>解决了<code>http</code>一个明显的问题&lt;安全&gt;,安全,还是TMD安全.</p><p>众所周知,<code>http</code>整个传输过程都是明文的,这样的结果就是传输过程中每一个节点都可以看到传输的是什么东西,甚至可以对其加以篡改.<code>https</code>就在这样的情况下诞生了,很合理.那为什么说<code>https</code>就很安全呢,因为它在传输过程中是加密的,双端使用密钥进行加密解密,在传输过程是无法被破译的.</p><section><h3>握手过程<a href="#握手过程"><span>#</span></a></h3><p>ssl的握手过程发生在tcp三次握手之后,所以性能肯定是比http拉的,别人三次握手之后就干活了,我们还需要生成密钥</p><ol>
<li>客户端发起握手,发送自己生成的随机数并附带自身支持的加密方式(加密套件)</li>
<li>服务端收到后同样返回一个自己生成的随机数,并发送证书(里边包含公钥)</li>
<li>客户端收到后通过证书加密生成一个预主私钥(其实就是新产生的一个随机数,如果面试官问你一共产生了几次随机数,答案是三次)</li>
<li>服务收到后使用私钥进行解密,得到预主私钥</li>
<li>双端各自在本地计算出对称加密所需要的密钥,由于这个是本地计算的,没有经历过网络传输,所以是安全的,之后双方就可以愉快的进行加密传输了</li>
</ol><p>整个过程如图下图:
<img src="./imgs/https握手过程.png" /></p></section><section><h3>你或许会疑惑,这样真的安全吗?<a href="#你或许会疑惑这样真的安全吗"><span>#</span></a></h3><p>你或许会想到,虽然所需要的密钥是本地计算的,我们无法获取,但是它的三个随机是经过了网络传输的,我们在中间劫持到这三个随机数,然后在使用和他们同样的算法岂不是可以生成一样的密钥?wow我们可真是个大聪明!盲星你发现了华子其实不然,因为你在传输第三个随机数的时候使用了服务端的公钥进行加密,但是破译却需要服务端的私钥,如果私钥没拿到,那么你根本无法得到第三个随机数,酱紫懂了吧;这也是<code>ssl</code>的一大特点,如果私钥泄露,那将毫无秘密可言~</p><p>又衍生一个问题,那抓包软件是咋获取到我的请求的,抓包软件可以解析你的请求是因为你信任了它的证书,它在中间先解密再用自己的密钥加密,双端是可以没有任何感知的</p></section></section>
<section><h2>拓展问题<a href="#拓展问题"><span>#</span></a></h2><p>相关的拓展问题</p><section><h3>TCP和UDP的区别<a href="#tcp和udp的区别"><span>#</span></a></h3><p>这个也很多地方问到,但是都是一些概念性的东西,我们需要做的是去了解一下他们的优缺点和区别,知道使用场景即可</p><section><h4>TCP的特点<a href="#tcp的特点"><span>#</span></a></h4><ul>
<li>面向链接&lt;在两端传输数据之前需要建立链接&gt;</li>
<li>仅支持单播传输&lt;仅支持一对一传输&gt;,也就是点对点</li>
<li>面向字节流&lt;字节流传输&gt;</li>
<li>可靠传输&lt;依靠tcp的段编号和确认号来判断是否数据丢包或者错误数据&gt;</li>
<li>拥有阻塞控制&lt;动态的调整速率和数量&gt;</li>
</ul></section><section><h4>UDP的特点<a href="#udp的特点"><span>#</span></a></h4><ul>
<li>面向无链接&lt;无需握手&gt;,想发就发,想收就收</li>
<li>有单播多播广播&lt;支持一对多&gt;,多对多,多对一等传输方式</li>
<li>面向报文&lt;数据都是通过一个一个报文源源不断传输&gt;</li>
<li>不可靠&lt;由于没有建立链接&gt;,数据不可靠,丢包等无法判断</li>
<li>头部信息小&lt;头部开销很小&gt;</li>
</ul></section><section><h4>对比<a href="#对比"><span>#</span></a></h4>

<table><thead><tr><th></th><th>TCP</th><th>UDP</th></tr></thead><tbody><tr><td>面向链接</td><td>YES</td><td>NO</td></tr><tr><td>可靠</td><td>YES</td><td>NO</td></tr><tr><td>传播方式</td><td>字节流</td><td>报文</td></tr><tr><td>传播对象</td><td>一对一</td><td>一对一,一对多,多对一,多对多</td></tr><tr><td>头部开销</td><td>20字节~60字节</td><td>8字节</td></tr><tr><td>使用场景</td><td>视频,直播,电话等</td><td>文件传输等</td></tr></tbody></table></section></section><section><h3>对称加密和非对称加密的区别<a href="#对称加密和非对称加密的区别"><span>#</span></a></h3><p>对称加密&lt;双方约定好一个密钥&gt;,双方加密解密都使用这个密钥来完成
公式:</p><ul>
<li>源文件+密钥=加密文件</li>
<li>加密文件-密钥=源文件</li>
</ul><p>非对称加密&lt;一方持有公钥&gt;,一方持有私钥,通过公钥加密的数据通过私钥来解密
公式:</p><ul>
<li>源文件+公钥=加密文件</li>
<li>加密文件-私钥=源文件</li>
</ul>

<table><thead><tr><th></th><th>对称加密</th><th>非对称加密</th></tr></thead><tbody><tr><td>安全</td><td>只要一方丢失密钥,GG</td><td>只要私钥不丢失即可</td></tr><tr><td>速度</td><td>由于算法,对称加密更快</td><td>算法相对复杂,慢一些</td></tr></tbody></table></section></section>
<section><h2>终于结束<a href="#终于结束"><span>#</span></a></h2><p>goodbye👋</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/react-class-zu-jian-de-sheng-ming-zhou-qi/</id>
      <title type="text">React class 组件的生命周期</title>
      <published>2022-08-09T09:08:59.000Z</published>
      <updated>2022-08-09T09:08:59.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/react-class-zu-jian-de-sheng-ming-zhou-qi/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>先提一下,只有class组件才会又声明周期,而函数式组件虽然可以通过hooks来模拟,但是并不能称之为生命周期,因为函数式组件不会创建对应的实例.</p>
<p>这里贴一个官方文档的链接:<a href="https://react.docschina.org/docs/react-component.html" target="_blank">React中文官方对生命周期的解释</a></p>
<p>然后还有一张图:<img src="./imgs/react组件生命周期.png" /></p>
<p>由图上的逻辑来看,我们可以分为三个大的部分来展开讲述这个生命周期,分别是:<code>挂载时</code>,<code>更新时</code>,<code>卸载时</code>.</p>
<section><h2>挂载时<a href="#挂载时"><span>#</span></a></h2><p>在图中可以看到,当一个组件从被创建到挂载到视图,生命周期调用如下:</p><ul>
<li>constructor()</li>
<li>static getDerivedStateFromProps()</li>
<li>render()</li>
<li>componentDidMount()</li>
</ul><p>其实在<code>componentDidMount()</code>之前还有一<code>UNSAFE_componentWillMount()</code>,但是由于react在引入切片后,这些钩子很可能被其他优先级更高的事物打断,导致重复执行,这是比较危险的.</p></section>
<section><h2>更新时<a href="#更新时"><span>#</span></a></h2><ul>
<li>static getDerivedStateFromProps()</li>
<li>shouldComponentUpdate()</li>
<li>render()</li>
<li>getSnapshotBeforeUpdate()</li>
<li>componentDidUpdate()</li>
</ul><p>废弃,原因同上:</p><ul>
<li>UNSAFE_componentWillUpdate()</li>
<li>UNSAFE_componentWillReceiveProps()</li>
</ul></section>
<section><h2>卸载时<a href="#卸载时"><span>#</span></a></h2><ul>
<li>componentWillUnmount()</li>
</ul></section>
<section><h2>错误处理<a href="#错误处理"><span>#</span></a></h2><ul>
<li>static getDerivedStateFromError()</li>
<li>componentDidCatch()</li>
</ul></section>
<section><h2>函数介绍<a href="#函数介绍"><span>#</span></a></h2><section><h3>constructor<a href="#constructor"><span>#</span></a></h3><p>这个钩子通常用来初始化state以及为事件函数绑定实例.形如下边的代码</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>this</span><span>.</span><span>state</span><span> </span><span>=</span><span> { </span><span>counter</span><span>:</span><span> </span><span>0</span><span> };</span></div></div><div><div><div>2</div></div><div><span>  </span><span>this</span><span>.</span><span>handleClick</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>handleClick</span><span>.</span><span>bind</span><span>(</span><span>this</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>不要在这个钩子里调用setState,你应该直接进行赋值操作,官方的意思这个钩子里应该避免引入任何副作用和订阅,如果有此需求,你应该放到componentDidMount里完成.</p></section><section><h3>static getDerivedStateFromProps(不常用)<a href="#static-getderivedstatefromprops不常用"><span>#</span></a></h3><p>如果你想要在初始化之后和更新之后都需要进行一段逻辑处理,那么这个钩子就很好用了.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>static</span><span> </span></span><span>getDerivedStateFromProps</span><span><span>(</span><span>props</span><span>, </span><span>state</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>会在调用 render 方法之前调用,在组件挂载或者更新后都会调用.它应返回一个对象来更新 state,如果返回 null 则不更新任何内容.</p></section><section><h3>render<a href="#render"><span>#</span></a></h3><p>是唯一必须去实现的方法,毕竟视图怎么显示是需要自己去定义的对吧.它是一个纯函数,他的返回值可以依赖于props和state进行变化.可以返回以下类型:</p><ul>
<li>React 元素。通常通过 JSX 创建。例如，<div> 会被 React 渲染为 DOM 节点， 会被 React 渲染为自定义组件，无论是 <div> 还是  均为 React 元素。</div></div></li>
<li>数组或 fragments。 使得 render 方法可以返回多个元素。欲了解更多详细信息，请参阅 fragments 文档。</li>
<li>Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息，请参阅有关 portals 的文档</li>
<li>字符串或数值类型。它们在 DOM 中会被渲染为文本节点</li>
<li>布尔类型或 null。什么都不渲染。（主要用于支持返回 test &amp;&amp;  的模式，其中 test 为布尔类型。)</li>
</ul><p>如果shouldComponentUpdate()方法里返回false的情况下,则不会去调用render.</p></section><section><h3>componentDidMount<a href="#componentdidmount"><span>#</span></a></h3><p>会在组件挂载后（插入 DOM 树中）立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据，此处是实例化请求的好地方。相当于是vue的mounted钩子.</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/yi-xie-javascript-shou-xie-ti-mu-shi-xian/</id>
      <title type="text">一些JavaScript手写题目实现</title>
      <published>2022-08-01T18:55:16.000Z</published>
      <updated>2022-08-01T18:55:16.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/yi-xie-javascript-shou-xie-ti-mu-shi-xian/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>开局<a href="#开局"><span>#</span></a></h2><p>不按分类顺序排放,我是持续加进去的,想到一个写一个属于是~</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>const</span><span> </span><span>isObject</span><span> </span><span>=</span><span><span> (</span><span>target</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span><span> </span><span>target</span><span> </span></span><span>!==</span><span> </span><span>null</span><span> </span><span>&amp;&amp;</span><span> </span><span>typeof</span><span><span> </span><span>target</span><span> </span></span><span>===</span><span> </span><span>"object"</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>const</span><span> </span><span>shallowCopy</span><span> </span><span>=</span><span><span> (</span><span>original</span><span>) </span></span><span>=&gt;</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>* 判断类型</span></div></div><div><div><div>9</div></div><div><span><span>   </span></span><span>*  基础类型 &gt; 直接返回</span></div></div><div><div><div>10</div></div><div><span><span>   </span></span><span>*  引用类型 &gt; copy</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>if</span><span> (</span><span>!</span><span>isObject</span><span><span>(</span><span>original</span><span>)) </span></span><span>return</span><span><span> </span><span>original</span><span>;</span></span></div></div><div><div><div>13</div></div><div><span>  </span><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>original</span><span>) </span></span><span>?</span><span> [] </span><span>:</span><span> {};</span></div></div><div><div><div>14</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>original</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span><span> (</span><span>original</span><span>.</span></span><span>hasOwnProperty</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>result</span><span>[</span><span>key</span><span>] </span><span>=</span><span><span> </span><span>original</span><span>[</span><span>key</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>return</span><span><span> </span><span>result</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>
</div></div><div><div><div>23</div></div><div><span>// 深拷贝</span></div></div><div><div><div>24</div></div><div><span>const</span><span> </span><span>deepCopy</span><span> </span><span>=</span><span><span> (</span><span>original</span><span>) </span></span><span>=&gt;</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>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>27</div></div><div><span>  </span><span>const</span><span> </span><span>nestedCopy</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>28</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>isObject</span><span><span>(</span><span>original</span><span>)) </span></span><span>return</span><span><span> </span><span>original</span><span>;</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>original</span><span>) </span></span><span>?</span><span> [] </span><span>:</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></span><span>map</span><span>.</span><span>set</span><span><span>(</span><span>original</span><span>, </span><span>result</span><span>);</span></span></div></div><div><div><div>32</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>original</span><span>) {</span></span></div></div><div><div><div>33</div></div><div><span>      </span><span>if</span><span><span> (</span><span>original</span><span>.</span></span><span>hasOwnProperty</span><span><span>(</span><span>key</span><span>)) {</span></span></div></div><div><div><div>34</div></div><div><span>        </span><span>const</span><span> </span><span>val</span><span> </span><span>=</span><span><span> </span><span>original</span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>35</div></div><div><span>        </span><span>if</span><span> (</span><span>isObject</span><span><span>(</span><span>val</span><span>)) {</span></span></div></div><div><div><div>36</div></div><div><span>          </span><span>// TODO 还需要判断原生对象类型,比如Date,你需要new一个Date;这里就不实现了</span></div></div><div><div><div>37</div></div><div><span>          </span><span>if</span><span><span> (</span><span>map</span><span>.</span></span><span>has</span><span><span>(</span><span>val</span><span>)) {</span></span></div></div><div><div><div>38</div></div><div><span>            </span><span>const</span><span> </span><span>cacheVal</span><span> </span><span>=</span><span><span> </span><span>map</span><span>.</span></span><span>get</span><span><span>(</span><span>val</span><span>);</span></span></div></div><div><div><div>39</div></div><div><span><span>            </span></span><span>result</span><span>[</span><span>key</span><span>] </span><span>=</span><span><span> </span><span>cacheVal</span><span>;</span></span></div></div><div><div><div>40</div></div><div><span><span>          </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>41</div></div><div><span>            </span><span>const</span><span> </span><span>copyVal</span><span> </span><span>=</span><span> </span><span>nestedCopy</span><span><span>(</span><span>val</span><span>);</span></span></div></div><div><div><div>42</div></div><div><span><span>            </span></span><span>result</span><span>[</span><span>key</span><span>] </span><span>=</span><span><span> </span><span>copyVal</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>} </span><span>else</span><span> {</span></div></div><div><div><div>45</div></div><div><span><span>          </span></span><span>result</span><span>[</span><span>key</span><span>] </span><span>=</span><span><span> </span><span>original</span><span>[</span><span>key</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></span><span>}</span></div></div><div><div><div>49</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>nestedCopy</span><span>();</span></div></div><div><div><div>52</div></div><div><span>  </span><span>// 置空缓存</span></div></div><div><div><div>53</div></div><div><span><span>  </span></span><span>map</span><span>.</span><span>clear</span><span>();</span></div></div><div><div><div>54</div></div><div><span>  </span><span>return</span><span><span> </span><span>result</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></section>
<section><h2>apply,call,bind<a href="#applycallbind"><span>#</span></a></h2><p>实现其中之一即可,bind 由于需要返回一个函数,并且会缓存参数,所以稍微复杂一点点</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>const</span><span> </span><span>_t</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>'panda'</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>sayName</span><span><span>(</span><span>age</span><span>, </span><span>sex</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>this</span><span>.</span><span>name</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>age</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>sex</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>Function</span><span>.</span><span>prototype</span><span>.</span><span>myApply</span><span> </span><span>=</span><span> </span><span>function</span><span><span> (</span><span>context</span><span>, </span><span>args</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>  </span><span>var</span><span><span> </span><span>t_obj</span><span> </span></span><span>=</span><span><span> </span><span>context</span><span> </span></span><span>||</span><span><span> </span><span>window</span><span>;</span></span></div></div><div><div><div>12</div></div><div><span>  </span><span>// 将fn作为对象的一个属性</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>t_obj</span><span>.</span><span>fn</span><span> </span><span>=</span><span> </span><span>this</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span><span> </span><span>t_obj</span><span>.</span></span><span>fn</span><span>(</span><span>...</span><span><span>args</span><span>)</span></span></div></div><div><div><div>16</div></div><div><span>  </span><span>delete</span><span><span> </span><span>t_obj</span><span>.</span></span><span>fn</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>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>19</div></div><div><span>};</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>Function</span><span>.</span><span>prototype</span><span>.</span><span>myBind</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>context</span><span>, </span></span><span>...</span><span><span>args</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>  </span><span>const</span><span> </span><span>self</span><span> </span><span>=</span><span> </span><span>this</span><span>;</span></div></div><div><div><div>23</div></div><div><span>  </span><span>return</span><span> </span><span>function</span><span>(</span><span>...</span><span><span>innerArgs</span><span>) {</span></span></div></div><div><div><div>24</div></div><div><span>    </span><span>return</span><span><span> </span><span>self</span><span>.</span></span><span>apply</span><span><span>(</span><span>context</span><span>, </span><span>args</span><span>.</span></span><span>concat</span><span><span>(</span><span>innerArgs</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>数组去重<a href="#数组去重"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 第一种 利用set的特性</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>duplicateRemoval</span><span>=</span><span><span>(</span><span>arr</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// 另外一种写法:Array.from(new Set(arr))</span></div></div><div><div><div>4</div></div><div><span> </span><span>return</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>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>// 第二种 双重循环,最笨方法(for+findIndex,for+find,filter+indexOf,for+includes是一样的原理)</span></div></div><div><div><div>7</div></div><div><span>const</span><span> </span><span>duplicateRemoval</span><span>=</span><span><span>(</span><span>arr</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>resultArr</span><span>=</span><span>[]</span></div></div><div><div><div>9</div></div><div><span>  </span><span>for</span><span>(</span><span>let</span><span><span> </span><span>i</span></span><span>=</span><span>0</span><span><span>;</span><span>i</span></span><span>&lt;</span><span><span>arr</span><span>.</span></span><span>length</span><span><span>;</span><span>i</span></span><span>++</span><span>){</span></div></div><div><div><div>10</div></div><div><span>    </span><span>const</span><span> </span><span>node</span><span>=</span><span><span>arr</span><span>[</span><span>i</span><span>]</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>let</span><span><span> </span><span>flag</span></span><span>=</span><span>false</span></div></div><div><div><div>12</div></div><div><span>    </span><span>for</span><span>(</span><span>let</span><span><span> </span><span>j</span></span><span>=</span><span>0</span><span><span>;</span><span>j</span></span><span>&lt;</span><span><span>resultArr</span><span>.</span></span><span>length</span><span><span>;</span><span>j</span></span><span>++</span><span>){</span></div></div><div><div><div>13</div></div><div><span>      </span><span>const</span><span> </span><span>node2</span><span>=</span><span><span>resultArr</span><span>[</span><span>j</span><span>]</span></span></div></div><div><div><div>14</div></div><div><span>      </span><span>if</span><span><span>(</span><span>node</span></span><span>===</span><span><span>node2</span><span>){</span></span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>flag</span><span>=</span><span>true</span></div></div><div><div><div>16</div></div><div><span>        </span><span>break</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>if</span><span>(</span><span>!</span><span><span>flag</span><span>)</span><span>resultArr</span><span>.</span></span><span>push</span><span><span>(</span><span>node</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>resultArr</span></span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div><span>// for + object(也可以用map)</span></div></div><div><div><div>24</div></div><div><span>const</span><span> </span><span>duplicateRemoval</span><span> </span><span>=</span><span><span>(</span><span>arr</span><span>)</span></span><span>=&gt;</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>const</span><span> </span><span>resultArr</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>27</div></div><div><span>  </span><span>const</span><span> </span><span>obj</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>28</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>&lt;</span><span><span>arr</span><span>.</span></span><span>length</span><span><span>;</span><span>i</span></span><span>++</span><span>){</span></div></div><div><div><div>29</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>obj</span><span>[</span><span>arr</span><span>[</span><span>i</span><span>]]) {</span></span></div></div><div><div><div>30</div></div><div><span><span>      </span></span><span>resultArr</span><span>.</span><span>push</span><span><span>(</span><span>arr</span><span>[</span><span>i</span><span>])</span></span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>obj</span><span>[</span><span>arr</span><span>[</span><span>i</span><span>]] </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>33</div></div><div><span><span>      </span></span><span>obj</span><span>[</span><span>arr</span><span>[</span><span>i</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>return</span><span><span> </span><span>resultArr</span></span></div></div><div><div><div>37</div></div><div><span>}</span></div></div><div><div><div>38</div></div><div><span>// 利用reduce</span></div></div><div><div><div>39</div></div><div><span>function</span><span> </span><span>duplicateRemoval</span><span><span> (</span><span>arr</span><span>) {</span></span></div></div><div><div><div>40</div></div><div><span>  </span><span>let</span><span><span> </span><span>resultArr</span><span> </span></span><span>=</span><span> []</span></div></div><div><div><div>41</div></div><div><span>  </span><span>return</span><span><span>  </span><span>arr</span><span>.</span></span><span>reduce</span><span><span>((</span><span>prev</span><span>, </span><span>next</span><span>,</span><span>index</span><span>, </span><span>arr</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>42</div></div><div><span>    </span><span>// 如果包含，就返回原数据，不包含，就把新数据追加进去</span></div></div><div><div><div>43</div></div><div><span>    </span><span>return</span><span><span> </span><span>newArr</span><span>.</span></span><span>includes</span><span><span>(</span><span>next</span><span>) </span></span><span>?</span><span><span> </span><span>newArr</span><span> </span></span><span>:</span><span><span>  </span><span>newArr</span><span>.</span></span><span>push</span><span><span>(</span><span>next</span><span>)</span></span></div></div><div><div><div>44</div></div><div><span><span>  </span></span><span>}, </span><span>0</span><span>)</span></div></div><div><div><div>45</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><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>const</span><span> </span><span>expandArray</span><span>=</span><span><span>(</span><span>array</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>3</div></div><div><span> </span><span>return</span><span><span> </span><span>array</span><span>.</span></span><span>flat</span><span>(</span><span>Infinity</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></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>expandArray</span><span> </span><span>=</span><span><span> (</span><span>array</span><span>, </span><span>level</span><span> </span></span><span>=</span><span> </span><span>Infinity</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span>  </span><span>const</span><span> </span><span>resultArr</span><span> </span><span>=</span><span> []</span></div></div><div><div><div>8</div></div><div><span>  </span><span>let</span><span><span> </span><span>dep</span><span> </span></span><span>=</span><span> </span><span>0</span></div></div><div><div><div>9</div></div><div><span>  </span><span>const</span><span> </span><span>run</span><span> </span><span>=</span><span><span> (</span><span>arr</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>// 每当需要递归调用说明层级+1了</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>dep</span><span>++</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>arr</span><span>.</span><span>forEach</span><span><span>((</span><span>node</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>13</div></div><div><span>      </span><span>if</span><span><span> (</span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>node</span><span>) </span></span><span>&amp;&amp;</span><span><span> </span><span>dep</span><span> </span></span><span>&lt;</span><span><span> </span><span>level</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>        </span><span>run</span><span><span>(</span><span>node</span><span>)</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>resultArr</span><span>.</span><span>push</span><span><span>(</span><span>node</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>// 一轮执行完毕,应该将层级减去1,恢复到上一层级</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>dep</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>run</span><span><span>(</span><span>array</span><span>)</span></span></div></div><div><div><div>23</div></div><div><span>  </span><span>return</span><span><span> </span><span>resultArr</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></section>
<section><h2>instanceof<a href="#instanceof"><span>#</span></a></h2><p>这个操作符用来判断该对象是否在某一条原型链上,通常用来判断是不是某个对象的子集</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>myInstanceof</span><span> </span><span>=</span><span><span> (</span><span>obj</span><span>, { </span><span>prototype</span><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>p</span><span> </span></span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>getPrototypeOf</span><span><span>(</span><span>obj</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>  </span><span>while</span><span> (</span><span>true</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span><span> (</span><span>p</span><span> </span></span><span>===</span><span> </span><span>null</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>      </span><span>return</span><span> </span><span>false</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><span>p</span><span> </span></span><span>===</span><span><span> </span><span>prototype</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>      </span><span>return</span><span> </span><span>true</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>p</span><span> </span><span>=</span><span><span> </span><span>Object</span><span>.</span></span><span>getPrototypeOf</span><span><span>(</span><span>p</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></section>
<section><h2>解析 url 参数<a href="#解析-url-参数"><span>#</span></a></h2><p>这块熟悉正则的话应该能玩的 6 一点</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>getUrlParams</span><span> </span><span>=</span><span><span> (</span><span>url</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>params</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>paramsString</span><span> </span><span>=</span><span><span> </span><span>url</span><span>.</span></span><span>split</span><span>(</span><span>'?'</span><span>)[</span><span>1</span><span>]</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>kvArray</span><span> </span><span>=</span><span><span> </span><span>paramsString</span><span>.</span></span><span>split</span><span>(</span><span>'&amp;'</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>kvArray</span><span>.</span><span>forEach</span><span><span>((</span><span>kv</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>lr</span><span> </span><span>=</span><span><span> </span><span>kv</span><span>.</span></span><span>split</span><span>(</span><span>'='</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>params</span><span>[</span><span>lr</span><span>[</span><span>0</span><span>]] </span><span>=</span><span><span> </span><span>lr</span><span>[</span></span><span>1</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>params</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>// 使用 URLSearchParams API 实现解析 URL 参数</span></div></div><div><div><div>13</div></div><div><span>const</span><span> </span><span>getUrlParams</span><span> </span><span>=</span><span><span> (</span><span>url</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span>  </span><span>const</span><span> </span><span>searchParams</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URLSearchParams</span><span><span>(</span><span>url</span><span>.</span></span><span>split</span><span>(</span><span>'?'</span><span>)[</span><span>1</span><span>]);</span></div></div><div><div><div>15</div></div><div><span>  </span><span>const</span><span> </span><span>params</span><span> </span><span>=</span><span> {};</span></div></div><div><div><div>16</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>searchParams</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>params</span><span>[</span><span>key</span><span>] </span><span>=</span><span><span> </span><span>value</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>return</span><span><span> </span><span>params</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></section>
<section><h2>Object.create<a href="#objectcreate"><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>createObj</span><span><span>(</span><span>obj</span><span>){</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>function</span><span> </span><span>Fn</span><span>(){}</span></div></div><div><div><div>3</div></div><div><span>  </span><span>Fn</span><span>.</span><span>prototype</span><span> </span><span>=</span><span><span> </span><span>obj</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Fn</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>实现简易 hash 路由系统<a href="#实现简易-hash-路由系统"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>myRoute</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>routes</span><span> </span><span>=</span><span> {}</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 当前hash值</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>currentHash</span><span> </span><span>=</span><span> </span><span>''</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>addEventListener</span><span>(</span><span>'load'</span><span>, </span><span>this</span><span>.</span><span>updateRoute</span><span>, </span><span>false</span><span>)</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>addEventListener</span><span>(</span><span>'hashchange'</span><span>, </span><span>this</span><span>.</span><span>updateRoute</span><span>, </span><span>false</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>addRoute</span><span><span>(</span><span>path</span><span>, </span><span>cb</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>this</span><span>.</span><span>routes</span><span><span>[</span><span>path</span><span>] </span></span><span>=</span><span><span> </span><span>cb</span><span> </span></span><span>||</span><span> </span><span>function</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></div></div><div><div><div>14</div></div><div><span>  </span><span>updateRoute</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>this</span><span>.</span><span>currentHash</span><span> </span><span>=</span><span><span> </span><span>location</span><span>.</span></span><span>hash</span><span>.</span><span>slice</span><span>(</span><span>1</span><span>) </span><span>||</span><span> </span><span>'/'</span></div></div><div><div><div>16</div></div><div><span>    </span><span>this</span><span>.</span><span>routes</span><span>[</span><span>this</span><span>.</span><span>currentHash</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>节流<a href="#节流"><span>#</span></a></h2><p>通常用来稀释函数执行的次数</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>d</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>delay</span><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>timer</span><span> </span></span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> (</span><span>...</span><span><span>args</span><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>timer</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>5</div></div><div><span><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>6</div></div><div><span>      </span><span>fn</span><span>(</span><span>...</span><span><span>args</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>timer</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}, </span><span>delay</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></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>const</span><span> </span><span>t</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>delay</span><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>timer</span><span> </span></span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> (</span><span>...</span><span><span>args</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>timer</span><span> </span><span>&amp;&amp;</span><span> </span><span>clearTimeout</span><span><span>(</span><span>timer</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span><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>6</div></div><div><span>      </span><span>fn</span><span>(</span><span>...</span><span><span>args</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}, </span><span>delay</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><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>t</span><span> </span><span>=</span><span><span> (</span><span>fn</span><span>, </span><span>delay</span><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>timer</span><span> </span></span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> (</span><span>...</span><span><span>args</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>timer</span><span> </span><span>&amp;&amp;</span><span> </span><span>clearTimeout</span><span><span>(</span><span>timer</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span><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>6</div></div><div><span>      </span><span>fn</span><span>(</span><span>...</span><span><span>args</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}, </span><span>delay</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>const</span><span> </span><span>req</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>const</span><span> </span><span>now</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>14</div></div><div><span>  </span><span>setTimeout</span><span>(</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>() </span><span>=&gt;</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><span>${</span><span>now</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>data</span><span>.</span><span>count</span><span> </span><span>=</span><span><span> </span><span>now</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></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>i</span><span> </span><span>===</span><span> </span><span>1</span><span> </span><span>?</span><span> </span><span>2000</span><span> </span><span>:</span><span> </span><span>0</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><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>26</div></div><div><span>  </span><span>count</span><span>:</span><span> </span><span>0</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>// 防抖函数,间隔500</span></div></div><div><div><div>30</div></div><div><span>const</span><span> </span><span>reqT</span><span> </span><span>=</span><span> </span><span>t</span><span><span>(</span><span>req</span><span>, </span></span><span>500</span><span>);</span></div></div><div><div><div>31</div></div><div><span>// 分别发送两次请求,必须满足防抖下执行两次的时间间隔</span></div></div><div><div><div>32</div></div><div><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>33</div></div><div><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>34</div></div><div><span><span>  </span></span><span>i</span><span>++</span><span>;</span></div></div><div><div><div>35</div></div><div><span>  </span><span>reqT</span><span>();</span></div></div><div><div><div>36</div></div><div><span><span>  </span></span><span>i</span><span> </span><span>===</span><span> </span><span>2</span><span> </span><span>&amp;&amp;</span><span> </span><span>clearInterval</span><span><span>(</span><span>timer</span><span>);</span></span></div></div><div><div><div>37</div></div><div><span>}, </span><span>700</span><span>);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>setTimeout</span><span>(</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"最终结果:"</span><span> </span><span>+</span><span><span> </span><span>data</span><span>.</span></span><span>count</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>// 最短需要700*2+2000才能看到最终结果</span></div></div><div><div><div>44</div></div><div><span>  </span><span>700</span><span> </span><span>*</span><span> </span><span>2</span><span> </span><span>+</span><span> </span><span>2000</span><span> </span><span>+</span><span> </span><span>1</span></div></div><div><div><div>45</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>通过上边的例子可以得出结果总是得到第一次响应慢的结果,而我们希望得到的最新发起请求的结果;怎么办呢?其实这里是一个典型的过期副作用问题,已在一篇文章中阐述过:<a href="https://pandavips.github.io/blogs/2024/guoqidefuzuoyong.html" target="_blank">https://pandavips.github.io/blogs/2024/guoqidefuzuoyong.html</a></p></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>class</span><span> </span><span>EventBus</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>_map</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>();</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>$on</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>on</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>this</span><span>.</span><span>$once</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>once</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>$off</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>off</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>$emit</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>emit</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>on</span><span><span>(</span><span>event</span><span>, </span><span>handler</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>const</span><span> </span><span>eventHandlers</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>_map</span><span>.</span><span>get</span><span><span>(</span><span>event</span><span>);</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>eventHandlers</span><span> </span></span><span>||</span><span><span> </span><span>eventHandlers</span><span>.</span></span><span>length</span><span> </span><span>===</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>      </span><span>this</span><span>.</span><span>_map</span><span>.</span><span>set</span><span><span>(</span><span>event</span><span>, [</span><span>handler</span><span>]);</span></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><span>eventHandlers</span><span>.</span><span>push</span><span><span>(</span><span>handler</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>once</span><span><span>(</span><span>event</span><span>, </span><span>handler</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>this</span><span>.</span><span>on</span><span><span>(</span><span>event</span><span>, (..</span></span><span>args</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>handler</span><span><span>(</span><span>args</span><span>);</span></span></div></div><div><div><div>20</div></div><div><span>      </span><span>this</span><span>.</span><span>off</span><span><span>(</span><span>event</span><span>, </span><span>handler</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>emit</span><span><span>(</span><span>event</span><span>, </span></span><span>...</span><span><span>args</span><span>) {</span></span></div></div><div><div><div>24</div></div><div><span>    </span><span>const</span><span> </span><span>eventHandlers</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>_map</span><span>.</span><span>get</span><span><span>(</span><span>event</span><span>);</span></span></div></div><div><div><div>25</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>eventHandlers</span><span> </span></span><span>||</span><span><span> </span><span>eventHandlers</span><span>.</span></span><span>length</span><span> </span><span>===</span><span> </span><span>0</span><span>) </span><span>return</span><span>;</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>eventHandlers</span><span>.</span><span>forEach</span><span><span>((</span><span>node</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span>      </span><span>node</span><span><span>(</span><span>args</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></span><span>}</span></div></div><div><div><div>30</div></div><div><span>  </span><span>off</span><span><span>(</span><span>event</span><span>, </span><span>handler</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span>    </span><span>const</span><span> </span><span>eventHandlers</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>_map</span><span>.</span><span>get</span><span><span>(</span><span>event</span><span>);</span></span></div></div><div><div><div>32</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>eventHandlers</span><span> </span></span><span>||</span><span><span> </span><span>eventHandlers</span><span>.</span></span><span>length</span><span> </span><span>===</span><span> </span><span>0</span><span>) </span><span>return</span><span>;</span></div></div><div><div><div>33</div></div><div><span>    </span><span>this</span><span>.</span><span>_map</span><span>.</span><span>set</span><span>(</span></div></div><div><div><div>34</div></div><div><span><span>      </span></span><span>event</span><span>,</span></div></div><div><div><div>35</div></div><div><span><span>      </span></span><span>eventHandlers</span><span>.</span><span>filter</span><span><span>((</span><span>node</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>36</div></div><div><span>        </span><span>return</span><span><span> </span><span>node</span><span> </span></span><span>!==</span><span><span> </span><span>handler</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>}</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><h2>mini 版本的 vuex<a href="#mini-版本的-vuex"><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>inject</span><span>, </span><span>reactive</span><span> } </span><span>from</span><span> </span><span>"vue"</span><span>;</span></div></div><div><div><div>2</div></div><div><span>// 注入时候的key,可以做当一个命名空间来理解,用户可以自行传入name做区分</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>STORE_KEY</span><span> </span><span>=</span><span> </span><span>"__store__"</span><span>;</span></div></div><div><div><div>4</div></div><div><span>// hooks</span></div></div><div><div><div>5</div></div><div><span>function</span><span> </span><span>useStore</span><span><span>(</span><span>name</span><span> </span></span><span>=</span><span> </span><span>STORE_KEY</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> </span><span>inject</span><span>(</span><span>STORE_KEY</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>// 创建实例</span></div></div><div><div><div>9</div></div><div><span>function</span><span> </span><span>createStore</span><span><span>(</span><span>options</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Store</span><span><span>(</span><span>options</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>class</span><span> </span><span>Store</span><span> {</span></div></div><div><div><div>13</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>options</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>this</span><span>.</span><span>$options</span><span> </span><span>=</span><span><span> </span><span>options</span><span>;</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>this</span><span>.</span><span>_state</span><span> </span><span>=</span><span> </span><span>reactive</span><span>({</span></div></div><div><div><div>16</div></div><div><span>      </span><span>data</span><span>:</span><span><span> </span><span>options</span><span>.</span></span><span>state</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>this</span><span>.</span><span>_name</span><span> </span><span>=</span><span><span> </span><span>options</span><span>.</span></span><span>name</span><span> </span><span>||</span><span> </span><span>STORE_KEY</span><span>;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>this</span><span>.</span><span>_mutations</span><span> </span><span>=</span><span><span> </span><span>options</span><span>.</span></span><span>mutations</span><span>;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>_actions</span><span> </span><span>=</span><span><span> </span><span>options</span><span>.</span></span><span>actions</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>get</span><span> </span><span>state</span><span>() {</span></div></div><div><div><div>23</div></div><div><span>    </span><span>return</span><span> </span><span>this</span><span>.</span><span>_state</span><span>.</span><span>data</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>// 触发mutation</span></div></div><div><div><div>26</div></div><div><span>  </span><span>commit</span><span> </span><span>=</span><span><span> (</span><span>type</span><span>, </span><span>payload</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span>    </span><span>const</span><span> </span><span>mutation</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>_mutations</span><span><span>[</span><span>type</span><span>];</span></span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>mutation</span><span> </span><span>&amp;&amp;</span><span> </span><span>mutation</span><span>(</span><span>this</span><span>.</span><span>state</span><span><span>, </span><span>payload</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>// 触发action</span></div></div><div><div><div>31</div></div><div><span>  </span><span>dispatch</span><span> </span><span>=</span><span><span> (</span><span>type</span><span>, </span><span>payload</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>32</div></div><div><span>    </span><span>const</span><span> </span><span>action</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>_actions</span><span><span>[</span><span>type</span><span>];</span></span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>action</span><span> </span><span>&amp;&amp;</span><span> </span><span>action</span><span>(</span><span>this</span><span><span>, </span><span>payload</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>// 注册的时候,vue会自动调用这个方法</span></div></div><div><div><div>36</div></div><div><span>  </span><span>install</span><span><span>(</span><span>app</span><span>) {</span></span></div></div><div><div><div>37</div></div><div><span>    </span><span>// 这里的app就是vue的实例,将状态注入</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>app</span><span>.</span><span>provide</span><span>(</span><span>this</span><span>.</span><span>_name</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></div></div><div><div><div>41</div></div><div><span>export</span><span> { </span><span>createStore</span><span>, </span><span>useStore</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><p>来源于<code>成都博智信息</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>const</span><span> </span><span>req</span><span> </span><span>=</span><span><span> (</span><span>url</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>time</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span> </span><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>5</div></div><div><span>    </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>Math</span><span>.</span><span>random</span><span>() </span><span>&gt;</span><span> </span><span>0.5</span></div></div><div><div><div>7</div></div><div><span>        </span><span>?</span><span> </span><span>resolve</span><span>({</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>url</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>time</span><span>,</span></div></div><div><div><div>10</div></div><div><span>            </span><span>done_time</span><span>:</span><span><span> </span><span>Date</span><span>.</span></span><span>now</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><span>reject</span><span>({</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>url</span><span>,</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>time</span><span>,</span></div></div><div><div><div>15</div></div><div><span>            </span><span>done_time</span><span>:</span><span><span> </span><span>Date</span><span>.</span></span><span>now</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><span>Math</span><span>.</span><span>random</span><span>() </span><span>*</span><span> </span><span>3000</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></div></div><div><div><div>20</div></div><div><span>/**</span></div></div><div><div><div>21</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>urls</span><span> 请求列表</span></div></div><div><div><div>22</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{*}</span><span> </span><span>max</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>const</span><span> </span><span>maxReq</span><span> </span><span>=</span><span><span> (</span><span>urls</span><span> </span></span><span>=</span><span><span> [], </span><span>max</span><span> </span></span><span>=</span><span> </span><span>3</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>25</div></div><div><span>  </span><span>const</span><span> </span><span>result</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>26</div></div><div><span>  </span><span>//请求完成数目,当前操作的索引</span></div></div><div><div><div>27</div></div><div><span>  </span><span>let</span><span><span> </span><span>count</span><span> </span></span><span>=</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>index</span><span> </span><span>=</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>29</div></div><div><span>  </span><span>const</span><span> </span><span>len</span><span> </span><span>=</span><span><span> </span><span>urls</span><span>.</span></span><span>length</span><span>;</span></div></div><div><div><div>30</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>const</span><span> </span><span>next</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>32</div></div><div><span>      </span><span>const</span><span> </span><span>index2</span><span> </span><span>=</span><span> </span><span>++</span><span><span>index</span><span>;</span></span></div></div><div><div><div>33</div></div><div><span>      </span><span>if</span><span><span> (</span><span>index2</span><span> </span></span><span>&gt;=</span><span><span> </span><span>len</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>34</div></div><div><span>      </span><span>const</span><span> </span><span>url</span><span> </span><span>=</span><span><span> </span><span>urls</span><span>[</span><span>index2</span><span>];</span></span></div></div><div><div><div>35</div></div><div><span>      </span><span>req</span><span><span>(</span><span>url</span><span>)</span></span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>.</span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>37</div></div><div><span><span>          </span></span><span>result</span><span>[</span><span>index2</span><span>] </span><span>=</span><span><span> </span><span>res</span><span>;</span></span></div></div><div><div><div>38</div></div><div><span>          </span><span>next</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><span>catch</span><span><span>((</span><span>err</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>41</div></div><div><span><span>          </span></span><span>result</span><span>[</span><span>index2</span><span>] </span><span>=</span><span><span> </span><span>err</span><span>;</span></span></div></div><div><div><div>42</div></div><div><span>          </span><span>next</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><span>finally</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>45</div></div><div><span>          </span><span>if</span><span> (</span><span>++</span><span><span>count</span><span> </span></span><span>&gt;=</span><span><span> </span><span>len</span><span>) </span></span><span>resolve</span><span><span>(</span><span>result</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>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>max</span><span> </span></span><span>&lt;</span><span><span> </span><span>len</span><span> </span></span><span>?</span><span><span> </span><span>max</span><span> </span></span><span>:</span><span><span> </span><span>len</span><span>); </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>      </span><span>next</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>});</span></div></div><div><div><div>52</div></div><div><span>};</span></div></div><div><div><div>53</div></div><div><span>const</span><span> </span><span>urls</span><span> </span><span>=</span><span> [</span><span>"url1"</span><span>, </span><span>"url2"</span><span>, </span><span>"url3"</span><span>, </span><span>"url4"</span><span>, </span><span>"url5"</span><span>, </span><span>"url6"</span><span>, </span><span>"url7"</span><span>];</span></div></div><div><div><div>54</div></div><div><span>// test</span></div></div><div><div><div>55</div></div><div><span>maxReq</span><span><span>(</span><span>urls</span><span>).</span></span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>56</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>57</div></div><div><span>  </span><span>debugger</span><span>;</span></div></div><div><div><div>58</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>class</span><span> </span><span>Scheduler</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 用于判断是否是一个promise,灵感来源于vue源码</span></div></div><div><div><div>3</div></div><div><span>  </span><span>static</span><span> </span><span>isPromise</span><span><span>(</span><span>val</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span><span> </span><span>val</span><span> </span></span><span>!==</span><span> </span><span>undefined</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>val</span><span> </span></span><span>!==</span><span> </span><span>null</span><span> </span><span>&amp;&amp;</span><span> </span><span>typeof</span><span><span> </span><span>val</span><span>.</span></span><span>then</span><span> </span><span>===</span><span> </span><span>"function"</span><span> </span><span>&amp;&amp;</span><span> </span><span>typeof</span><span><span> </span><span>val</span><span>.</span></span><span>catch</span><span> </span><span>===</span><span> </span><span>"function"</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>constructor</span><span><span>(</span><span>limit</span><span> </span></span><span>=</span><span> </span><span>2</span><span>, </span><span>callback</span><span> </span><span>=</span><span> () </span><span>=&gt;</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>this</span><span>.</span><span>index</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>// 任务队列</span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>queue</span><span> </span><span>=</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>doneCount</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 限制并发数</span></div></div><div><div><div>14</div></div><div><span>    </span><span>this</span><span>.</span><span>limit</span><span> </span><span>=</span><span><span> </span><span>limit</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>this</span><span>.</span><span>resultArray</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>this</span><span>.</span><span>callback</span><span> </span><span>=</span><span><span> </span><span>callback</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></div></div><div><div><div>21</div></div><div><span>  </span><span>add</span><span> </span><span>=</span><span><span> (</span><span>task</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>index</span><span>++</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span><span> (</span><span>Scheduler</span><span>.</span></span><span>isPromise</span><span><span>(</span><span>task</span><span>)) {</span></span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"任务是promise类型"</span><span>);</span></div></div><div><div><div>25</div></div><div><span>      </span><span>this</span><span>.</span><span>queue</span><span>.</span><span>push</span><span>(</span><span>this</span><span>.</span><span>decoratTask</span><span><span>(</span><span>task</span><span>));</span></span></div></div><div><div><div>26</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>task</span><span> </span></span><span>===</span><span> </span><span>"function"</span><span>) {</span></div></div><div><div><div>27</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>"你似乎传入的不是一个promise,这种情况请查看下使用方式."</span><span>);</span></div></div><div><div><div>28</div></div><div><span>      </span><span>// 这里需要遵循一定的规范去使用,需要提前告知使用者,实际上还不如强制告诉使用者必须传入promise</span></div></div><div><div><div>29</div></div><div><span>      </span><span>this</span><span>.</span><span>queue</span><span>.</span><span>push</span><span>(</span></div></div><div><div><div>30</div></div><div><span>        </span><span>this</span><span>.</span><span>decoratTask</span><span>(</span></div></div><div><div><div>31</div></div><div><span>          </span><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>32</div></div><div><span>            </span><span>task</span><span><span>(</span><span>resolve</span><span>, </span><span>reject</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><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></div></div><div><div><div>39</div></div><div><span>  </span><span>start</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>40</div></div><div><span>    </span><span>const</span><span> </span><span>count</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>limit</span><span> </span><span>&gt;</span><span> </span><span>this</span><span>.</span><span>queue</span><span>.</span><span>length</span><span> </span><span>?</span><span> </span><span>this</span><span>.</span><span>queue</span><span>.</span><span>length</span><span> </span><span>:</span><span> </span><span>this</span><span>.</span><span>limit</span><span>;</span></div></div><div><div><div>41</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>count</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>42</div></div><div><span>      </span><span>this</span><span>.</span><span>next</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></div></div><div><div><div>46</div></div><div><span>  </span><span>next</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>47</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>queue</span><span>.</span><span>length</span><span>) {</span></div></div><div><div><div>48</div></div><div><span>      </span><span>const</span><span> </span><span>task</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>queue</span><span>.</span><span>shift</span><span>();</span></div></div><div><div><div>49</div></div><div><span>      </span><span>this</span><span>.</span><span>run</span><span><span>(</span><span>task</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>};</span></div></div><div><div><div>52</div></div><div><span>  </span><span>// 装饰任务对象,其实这里的作用是为了保存结果数组的有序性</span></div></div><div><div><div>53</div></div><div><span>  </span><span>decoratTask</span><span> </span><span>=</span><span><span> (</span><span>task</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>54</div></div><div><span>    </span><span>return</span><span> {</span></div></div><div><div><div>55</div></div><div><span>      </span><span>index</span><span>:</span><span> </span><span>this</span><span>.</span><span>index</span><span>,</span></div></div><div><div><div>56</div></div><div><span>      </span><span>p</span><span>:</span><span><span> </span><span>task</span><span>,</span></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>};</span></div></div><div><div><div>59</div></div><div><span>  </span><span>// 执行单次任务</span></div></div><div><div><div>60</div></div><div><span>  </span><span>run</span><span> </span><span>=</span><span><span> (</span><span>task</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"run"</span><span>);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>const</span><span> { </span><span>index</span><span>, </span><span>p</span><span> } </span><span>=</span><span><span> </span><span>task</span><span>;</span></span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>p</span><span>.</span><span>then</span><span><span>((</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>64</div></div><div><span>      </span><span>this</span><span>.</span><span>resultArray</span><span><span>[</span><span>index</span><span>] </span></span><span>=</span><span><span> </span><span>res</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>.</span><span>catch</span><span><span>((</span><span>err</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>err</span><span>);</span></span></div></div><div><div><div>68</div></div><div><span>        </span><span>this</span><span>.</span><span>resultArray</span><span><span>[</span><span>index</span><span>] </span></span><span>=</span><span><span> </span><span>err</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>      </span></span><span>.</span><span>finally</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>71</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"will next"</span><span>);</span></div></div><div><div><div>72</div></div><div><span>        </span><span>this</span><span>.</span><span>doneCount</span><span>++</span><span>;</span></div></div><div><div><div>73</div></div><div><span>        </span><span>if</span><span> (</span><span>this</span><span>.</span><span>index</span><span> </span><span>+</span><span> </span><span>1</span><span> </span><span>===</span><span> </span><span>this</span><span>.</span><span>doneCount</span><span>) {</span></div></div><div><div><div>74</div></div><div><span>          </span><span>this</span><span>.</span><span>callback</span><span>(</span><span>this</span><span>.</span><span>resultArray</span><span>);</span></div></div><div><div><div>75</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>76</div></div><div><span>          </span><span>this</span><span>.</span><span>next</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><span>}</span></div></div><div><div><div>81</div></div><div><span>const</span><span> </span><span>req</span><span> </span><span>=</span><span><span> (</span><span>url</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>82</div></div><div><span>  </span><span>const</span><span> </span><span>time</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>83</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>84</div></div><div><span>    </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>85</div></div><div><span>      </span><span>resolve</span><span>({</span></div></div><div><div><div>86</div></div><div><span><span>        </span></span><span>url</span><span>,</span></div></div><div><div><div>87</div></div><div><span><span>        </span></span><span>time</span><span>,</span></div></div><div><div><div>88</div></div><div><span>        </span><span>done_time</span><span>:</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>(),</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><span><span>    </span></span><span>}, </span><span>Math</span><span>.</span><span>random</span><span>() </span><span>*</span><span> </span><span>3000</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></div></div><div><div><div>93</div></div><div><span>const</span><span> </span><span>scheduler</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Scheduler</span><span>(</span><span>99</span><span><span>, (</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>94</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>95</div></div><div><span>});</span></div></div><div><div><div>96</div></div><div><span>const</span><span> </span><span>urls</span><span> </span><span>=</span><span> [</span><span>"url1"</span><span>, </span><span>"url2"</span><span>, </span><span>"url3"</span><span>, </span><span>"url4"</span><span>, </span><span>"url5"</span><span>, </span><span>"url6"</span><span>, </span><span>"url7"</span><span>];</span></div></div><div><div><div>97</div></div><div><span><span>urls</span><span>.</span></span><span>forEach</span><span><span>((</span><span>url</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>98</div></div><div><span><span>  </span></span><span>scheduler</span><span>.</span><span>add</span><span>(</span><span>req</span><span><span>(</span><span>url</span><span>));</span></span></div></div><div><div><div>99</div></div><div><span>});</span></div></div><div><div><div>100</div></div><div><span><span>scheduler</span><span>.</span></span><span>start</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><p>实现一个高阶函数<code>pipe</code>,让传递的函数依次执行,上一个函数的返回值是下一个函数的参数;形如这样调用(fn,fn,fn,fn,fn)(params)</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>pipe</span><span> </span><span>=</span><span> (</span><span>...</span><span><span>fns</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span><span> (</span><span>params</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>let</span><span><span> </span><span>result</span><span> </span></span><span>=</span><span><span> </span><span>params</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>[</span><span>...</span><span><span>fns</span><span>].</span></span><span>map</span><span><span>((</span><span>fn</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>result</span><span> </span><span>=</span><span> </span><span>fn</span><span><span>(</span><span>result</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>return</span><span><span> </span><span>result</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>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>pipe</span><span>(</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>(</span><span>a</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span><span> </span><span>a</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></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>(</span><span>a</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>return</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span> </span><span>2</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>0</span><span>);</span></div></div><div><div><div>18</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>res</span><span>); </span></span><span>//3</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>如果函数内有异步行为的话，那么返回的结果也会是一个 promise,稍加改造即可。</p></section>
<section><h2>手写一个符合 promiseA+规范的 Promise<a href="#手写一个符合-promisea规范的-promise"><span>#</span></a></h2><p>关于如何手写一个符合 PromiseA+ 规范的 Promise 实现，我已经在另一篇文章中详细讨论过了。你可以在以下链接中找到完整的实现和解释:</p><p><a href="https://pandavips.github.io/blogs/2023/liaoyiliaoPromise.html" target="_blank">手写一个符合 PromiseA+ 规范的 Promise</a></p><p>这篇文章深入探讨了 Promise 的内部工作原理，并提供了一个完整的、符合规范的实现。我建议你查看该文章以获取更详细的信息和代码示例。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/jia-xiao-kao-ke-mu-yi-shi-ji-xia-de-bi-ji/</id>
      <title type="text">驾校-考科目一时记下的笔记</title>
      <published>2022-07-16T22:18:47.000Z</published>
      <updated>2022-07-16T22:18:47.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/jia-xiao-kao-ke-mu-yi-shi-ji-xia-de-bi-ji/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>自定义名词解释<a href="#自定义名词解释"><span>#</span></a></h2>

<table><thead><tr><th>名词</th><th>解释</th></tr></thead><tbody><tr><td>普通车,普车</td><td>校车,危险运输车,客运车以外的车</td></tr><tr><td>校客危,三车</td><td>校车,危险运输车,客运车</td></tr><tr><td>客危</td><td>客运车,危险运输车</td></tr><tr><td>普路</td><td>城市快速路,高速路以外的路</td></tr><tr><td>速路</td><td>城市快速路,高速路</td></tr><tr><td>顶扣</td><td>扣满十二分</td></tr><tr><td>违</td><td>违反,不按规定</td></tr></tbody></table></section>
<section><h2>口诀隐式规则<a href="#口诀隐式规则"><span>#</span></a></h2><ul>
<li>都会遵循<code>所有车&gt;普通车&gt;校车&gt;客车&gt;货车&gt;危险运输车&gt;三车</code>这样的顺序来编写(不过一般都会考虑将车型编进口诀,知道这么个东西就行)</li>
</ul></section>
<section><h2>扣分题<a href="#扣分题"><span>#</span></a></h2><section><h3>1分<a href="#1分"><span>#</span></a></h3><ol>
<li>不系安全带</li>
<li>摩托车不带头盔</li>
<li>不按规定会车；普通路上违规倒车掉头</li>
<li>不按规定使用灯光</li>
<li>违反禁令标志、禁止标线（如：压双黄线、单黄线,包含违规停车）</li>
<li>载货长度、宽度、高度超过规定的</li>
<li>【没安全检验的普通车，扣1分】驾驶未按规定定期进行安全技术检验的公路客运汽车、旅游客运汽车、危险物品运输车辆以外的机动车上道路行驶的，一次记1分。</li>
<li>超载未达30%</li>
<li>【校客危，普通道路超速10%-20%】驾驶校车、中型以上载客载货汽车、危险物品运输车辆在普通路上超速10%-20%</li>
</ol><p>口诀: 违会掉灯盔带禁,普车不检货过三,十载三车速一二</p></section><section><h3>3分<a href="#3分"><span>#</span></a></h3><ol>
<li>普通车载人超载20%-50%；</li>
<li>普通车在普通路上超速20%-50%；</li>
<li>货车超载30%-50%的，或者违反规定载客的；</li>
<li>（所有车）在高速公路或者城市快速路上不按规定车道行驶的；</li>
<li>不按规定超车、让行；普通路上逆行的；</li>
<li>借道超车或者占用对面车道、穿插等候车辆的；</li>
<li>打电话</li>
<li>行经人行横道不按规定减速、停车、避让行人的；</li>
<li>不按规定避让校车的；</li>
<li>驾驶不按规定安装机动车号牌的机动车上道路行驶的；</li>
<li>在道路上车辆发生故障、事故停车后，不按规定使用灯光或者设置警告标志的；</li>
<li>【没安全检验的“客危”，扣3分】驾驶未按规定定期进行安全技术检验的公路客运汽车、旅游客运汽车、危险物品运输车辆上道路行驶的；</li>
<li>【校车没安检或存在安全隐患】驾驶校车上道路行驶前，未对校车车况是否符合安全技术要求进行检查，或者驾驶存在安全隐患的校车上道路行驶的；</li>
<li>【货车疲劳驾驶】连续驾驶载货汽车超过4小时未停车休息或者停车休息时间少于20分钟的；</li>
<li>【在高速上龟速行使】驾驶机动车在高速公路上行驶低于规定最低时速的。</li>
</ol><p>口诀&lt;违道超让校电逆&gt;,借占道穿不让人,速路行驶速低规(龟),牌乱故停违灯标,普车超载十二五,它走普路超速同,校患未检货未检,42不休让货(祸)行,超载三五货违客</p></section><section><h3>6分<a href="#6分"><span>#</span></a></h3><ol>
<li>驾驶校车、公路客运汽车、旅游客运汽车载人超载未达到20%；</li>
<li>驾驶7座以上载客汽车载人超载20%-50%；</li>
<li>其他普通车载人超载50%-100%的；</li>
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆在高速公路、城市快速路上超速未达到20%，或者在普通路上超速20%-50%的；</li>
<li>普通车在高速公路、城市快速路上超速20%-50%；在普通路上超速50%以上的；</li>
<li>货车超载50%以上的；</li>
<li>【运危险品没挂警示标志】驾驶机动车载运爆炸物品、易燃易爆化学物品以及剧毒、放射性等危险物品，未按指定的时间、路线、速度行驶或者未悬挂警示标志并采取必要的安全措施的；</li>
<li>【闯红灯】驾驶机动车不按交通信号灯指示通行的；</li>
<li>机动车驾驶证被暂扣或者扣留期间驾驶机动车的；</li>
<li>造成致人轻微伤或者财产损失的交通事故后逃逸，尚不构成犯罪的；</li>
<li>违法占用应急车道</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/za-xiang-bi-ji-hui-zong/</id>
      <title type="text">杂项笔记汇总</title>
      <published>2022-07-10T21:38:40.000Z</published>
      <updated>2022-07-10T21:38:40.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/za-xiang-bi-ji-hui-zong/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>这是一个零散的笔记汇总<a href="#这是一个零散的笔记汇总"><span>#</span></a></h2><p>好记性不如烂笔头,记一些杂乱的东西,避免了每次都要去百度的尴尬境地</p></section>
<section><h2>地址收集(避免每次都要去搜一下,恼火Ï)<a href="#地址收集避免每次都要去搜一下恼火ï"><span>#</span></a></h2><ul>
<li><a href="https://github.com/coreybutler/nvm-windows" target="_blank">nvm win地址</a></li>
<li><a href="https://learn.microsoft.com/zh-cn/windows/wsl/about" target="_blank">微软wsl2</a></li>
<li><a href="https://msdn.itellyou.cn/" target="_blank">win镜像地址</a></li>
</ul></section>
<section><h2>设置淘宝镜像(不建议,建议使用下面的方式)<a href="#设置淘宝镜像不建议建议使用下面的方式"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 已经废弃</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npm.taobao.org</span></div></div><div><div><div>3</div></div><div><span># 最新的taobao镜像地址</span></div></div><div><div><div>4</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>http://registry.npmmirror.com</span></div></div><div><div><div>5</div></div><div><span># 恢复到npm官方镜像地址</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npmjs.org</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>NRM(牛肉面)设置NPM的镜像地址<a href="#nrm牛肉面设置npm的镜像地址"><span>#</span></a></h2><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>i</span><span> </span><span>nrm</span><span> </span><span>-g</span></div></div><div><div><div>2</div></div><div><span># 列出镜像地址</span></div></div><div><div><div>3</div></div><div><span>nrm</span><span> </span><span>ls</span></div></div><div><div><div>4</div></div><div><span># 使用镜像地址</span></div></div><div><div><div>5</div></div><div><span>nrm</span><span> </span><span>use</span><span> </span><span>taobao</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>删除整个依赖库node_modules<a href="#删除整个依赖库node_modules"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 如果没有安装需要先安装这个</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>rimraf</span><span> </span><span>-g</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>rimraf</span><span> </span><span>node_modules</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 清空npm缓存</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>clean</span><span> </span><span>--force</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>VMware 虚拟机固定IP<a href="#vmware-虚拟机固定ip"><span>#</span></a></h2><p>1.先将虚拟机网络连接模式改为NAT
2.设置NAT网关
3.设置DHCP
4.将虚拟机系统设置为静态ip,ip要对应前边设置的网关</p><p>centos示例:</p><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/etc/sysconfig/network-scripts/ifcfg-ens33</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>TYPE</span><span>=</span><span>Ethernet</span></div></div><div><div><div>4</div></div><div><span>PROXY_METHOD</span><span>=</span><span>none</span></div></div><div><div><div>5</div></div><div><span>BROWSER_ONLY</span><span>=</span><span>no</span></div></div><div><div><div>6</div></div><div><span>BOOTPROTO</span><span>=</span><span>static</span></div></div><div><div><div>7</div></div><div><span>DEFROUTE</span><span>=</span><span>yes</span></div></div><div><div><div>8</div></div><div><span>IPV4_FAILURE_FATAL</span><span>=</span><span>no</span></div></div><div><div><div>9</div></div><div><span>IPV6INIT</span><span>=</span><span>yes</span></div></div><div><div><div>10</div></div><div><span>IPV6_AUTOCONF</span><span>=</span><span>yes</span></div></div><div><div><div>11</div></div><div><span>IPV6_DEFROUTE</span><span>=</span><span>yes</span></div></div><div><div><div>12</div></div><div><span>IPV6_FAILURE_FATAL</span><span>=</span><span>no</span></div></div><div><div><div>13</div></div><div><span>IPV6_ADDR_GEN_MODE</span><span>=</span><span>stable-privacy</span></div></div><div><div><div>14</div></div><div><span>NAME</span><span>=</span><span>ens33</span></div></div><div><div><div>15</div></div><div><span>UUID</span><span>=</span><span>1f6d2414-12b7-40ef-8fb1-d2e6db9c739b</span></div></div><div><div><div>16</div></div><div><span>DEVICE</span><span>=</span><span>ens33</span></div></div><div><div><div>17</div></div><div><span>ONBOOT</span><span>=</span><span>yes</span></div></div><div><div><div>18</div></div><div><span>IPADDR</span><span>=</span><span>192.168.13.130</span></div></div><div><div><div>19</div></div><div><span>NETMASK</span><span>=</span><span>255.255.255.0</span></div></div><div><div><div>20</div></div><div><span>GATEWAY</span><span>=</span><span>192.168.13.254</span></div></div><div><div><div>21</div></div><div><span>DNS1</span><span>=</span><span>114.114.114.114</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>ubuntu22.04换源<a href="#ubuntu2204换源"><span>#</span></a></h2><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>/etc/apt/</span></div></div><div><div><div>2</div></div><div><span>cp</span><span> </span><span>sources.list</span><span> </span><span>sources.list_bak</span></div></div><div><div><div>3</div></div><div><span>vim</span><span> </span><span>sources.list</span><span> </span><span>#用下边内容覆盖</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>6</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>7</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-security</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>8</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-security</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>9</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-updates</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>10</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-updates</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>11</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-proposed</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>12</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-proposed</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>13</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-backports</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>14</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/ubuntu/</span><span> </span><span>jammy-backports</span><span> </span><span>main</span><span> </span><span>restricted</span><span> </span><span>universe</span><span> </span><span>multiverse</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>apt</span><span> </span><span>update</span></div></div><div><div><div>17</div></div><div><span>apt</span><span> </span><span>upgrade</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>debian换阿里云源<a href="#debian换阿里云源"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/debian/</span><span> </span><span>stretch</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>2</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/debian/</span><span> </span><span>stretch-updates</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>3</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/debian/</span><span> </span><span>stretch-backports</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>4</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/debian/</span><span> </span><span>stretch</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>5</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/debian/</span><span> </span><span>stretch-updates</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>6</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/debian/</span><span> </span><span>stretch-backports</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>7</div></div><div><span>deb</span><span> </span><span>http://mirrors.aliyun.com/debian-security/</span><span> </span><span>stretch/updates</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div><div><div><div>8</div></div><div><span>deb-src</span><span> </span><span>http://mirrors.aliyun.com/debian-security/</span><span> </span><span>stretch/updates</span><span> </span><span>main</span><span> </span><span>non-free</span><span> </span><span>contrib</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Linux将目录设置为所有人都能读写<a href="#linux将目录设置为所有人都能读写"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># &lt;!-- 用chmod命令, 先进入要修改权限的目录abc的上一级目录 , --&gt;</span></div></div><div><div><div>2</div></div><div><span># &lt;!-- 然后运行 --&gt;</span></div></div><div><div><div>3</div></div><div><span>chmod</span><span> </span><span>-R</span><span> </span><span>o+r+w</span><span> </span><span>abc</span></div></div><div><div><div>4</div></div><div><span># &lt;!-- 再运行 --&gt;</span></div></div><div><div><div>5</div></div><div><span>chmod</span><span> </span><span>o+x+w</span><span> </span><span>abc</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><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 开启</span></div></div><div><div><div>2</div></div><div><span>ufw</span><span> </span><span>enable</span></div></div><div><div><div>3</div></div><div><span># 关闭</span></div></div><div><div><div>4</div></div><div><span>ufw</span><span> </span><span>disable</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>生成ssh key<a href="#生成ssh-key"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh-keygen</span><span> </span><span>-t</span><span> </span><span>rsa</span><span> </span><span>-C</span><span> </span><span>"919401990@qq.com"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>debian11 一些常见的编译环境<a href="#debian11-一些常见的编译环境"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>pkg-config</span><span> </span><span>make</span><span> </span><span>libpng-dev</span><span> </span><span>gcc</span><span> </span><span>patch</span><span> </span><span>-y</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>创建react typescript项目<a href="#创建react-typescript项目"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>dlx</span><span> </span><span>create-react-app</span><span> </span><span>pd-project</span><span> </span><span>--template</span><span> </span><span>typescript</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>解决NPM下载二进制巨慢的问题<a href="#解决npm下载二进制巨慢的问题"><span>#</span></a></h2><p>在<code>.npmrc</code>文件内加入以下内容,从<code>antfu</code>博客上看来的,经过测试过相当快~
原文链接:<a href="https://antfu.me/posts/npm-binary-mirrors" target="_blank">https://antfu.me/posts/npm-binary-mirrors</a></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>NODEJS_ORG_MIRROR=https://cdn.npmmirror.com/binaries/node</span></div></div><div><div><div>2</div></div><div><span>NVM_NODEJS_ORG_MIRROR=https://cdn.npmmirror.com/binaries/node</span></div></div><div><div><div>3</div></div><div><span>PHANTOMJS_CDNURL=https://cdn.npmmirror.com/binaries/phantomjs</span></div></div><div><div><div>4</div></div><div><span>CHROMEDRIVER_CDNURL=https://cdn.npmmirror.com/binaries/chromedriver</span></div></div><div><div><div>5</div></div><div><span>OPERADRIVER_CDNURL=https://cdn.npmmirror.com/binaries/operadriver</span></div></div><div><div><div>6</div></div><div><span>ELECTRON_MIRROR=https://cdn.npmmirror.com/binaries/electron/</span></div></div><div><div><div>7</div></div><div><span>ELECTRON_BUILDER_BINARIES_MIRROR=https://cdn.npmmirror.com/binaries/electron-builder-binaries/</span></div></div><div><div><div>8</div></div><div><span>SASS_BINARY_SITE=https://cdn.npmmirror.com/binaries/node-sass</span></div></div><div><div><div>9</div></div><div><span>SWC_BINARY_SITE=https://cdn.npmmirror.com/binaries/node-swc</span></div></div><div><div><div>10</div></div><div><span>NWJS_URLBASE=https://cdn.npmmirror.com/binaries/nwjs/v</span></div></div><div><div><div>11</div></div><div><span>PUPPETEER_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries</span></div></div><div><div><div>12</div></div><div><span>SENTRYCLI_CDNURL=https://cdn.npmmirror.com/binaries/sentry-cli</span></div></div><div><div><div>13</div></div><div><span>SAUCECTL_INSTALL_BINARY_MIRROR=https://cdn.npmmirror.com/binaries/saucectl</span></div></div><div><div><div>14</div></div><div><span>npm_config_sharp_binary_host=https://cdn.npmmirror.com/binaries/sharp</span></div></div><div><div><div>15</div></div><div><span>npm_config_sharp_libvips_binary_host=https://cdn.npmmirror.com/binaries/sharp-libvips</span></div></div><div><div><div>16</div></div><div><span>npm_config_robotjs_binary_host=https://cdn.npmmirror.com/binaries/robotj</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>win ltsc激活<a href="#win-ltsc激活"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>slmgr</span><span> </span><span>-ipk</span><span> </span><span>M7XTQ-FN8P6-TTKYV-9D4CC-J462D</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>slmgr</span><span> </span><span>-skms</span><span> </span><span>kms.03k.org</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>slmgr</span><span> </span><span>-ato</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>slmgr</span><span> </span><span>-dlv</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>git相关的<a href="#git相关的"><span>#</span></a></h2><section><h3>git一次性拉去所有分支更新组合拳<a href="#git一次性拉去所有分支更新组合拳"><span>#</span></a></h3><p>一次性采集所有分支,并进行pull更新</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>fetch</span><span> </span><span>--all</span></div></div><div><div><div>2</div></div><div><span># 项目</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>pull</span><span> </span><span>--all</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>追踪文件名大小写<a href="#追踪文件名大小写"><span>#</span></a></h3><p>由于默认的git不会追踪文件大小写,这会导致一些问题,白白浪费我们的时间来排查问题,我们可以防患于未然,执行以下命令在未来杜绝这个问题</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 全局</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>config</span><span> </span><span>--global</span><span> </span><span>core.ignorecase</span><span> </span><span>false</span></div></div><div><div><div>3</div></div><div><span># 项目</span></div></div><div><div><div>4</div></div><div><span>git</span><span> </span><span>config</span><span> </span><span>core.ignorecase</span><span> </span><span>false</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>给linux文件添加可执行权限<a href="#给linux文件添加可执行权限"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>chmod</span><span> </span><span>u+x</span><span> </span><span>xxx.sh</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>./xxx.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>git 只拉取最新的提交,git clone速度拉满<a href="#git-只拉取最新的提交git-clone速度拉满"><span>#</span></a></h2><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>clone</span><span> </span><span>-b</span><span> </span><span>develop</span><span> </span><span>git@xxx.org:username/repo.git</span><span> </span><span>--depth=1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>禁用ts检查<a href="#禁用ts检查"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>单行忽略</span><span><span>(</span><span>添加到特定行的行前来忽略这一行的错误</span><span>)</span></span></div></div><div><div><div>2</div></div><div><span>// @ts-ignore</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>添加到该文件的首行才起作用</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span>// @ts-nocheck</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>// @ts-check</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>adb连接设备<a href="#adb连接设备"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>adb</span><span> </span><span>connect</span><span> [ip]:[port]</span></div></div><div><div><div>2</div></div><div><span># 比如</span></div></div><div><div><div>3</div></div><div><span>adb</span><span> </span><span>connect</span><span> </span><span>100.78.252.21:5555</span></div></div><div><div><div>4</div></div><div><span># 断开</span></div></div><div><div><div>5</div></div><div><span>adb</span><span> </span><span>disconnect</span><span> [ip]:[port]</span></div></div><div><div><div>6</div></div><div><span># 使用adb远程控制手机</span></div></div><div><div><div>7</div></div><div><span>scrcpy</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>配置bash终端代理<a href="#配置bash终端代理"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></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>http_proxy</span><span>=</span><span>http</span><span>://</span><span>127</span><span>.</span><span>0</span><span>.</span><span>0</span><span>.</span><span>1</span><span>:</span><span>7890</span><span>;</span></div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>https_proxy</span><span>=</span><span>http</span><span>://</span><span>127</span><span>.</span><span>0</span><span>.</span><span>0</span><span>.</span><span>1</span><span>:</span><span>7890</span><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><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>3秒</span></div></div><div><div><div>2</div></div><div><span>https://www.w3school.com.cn/i/movie.ogg</span></div></div><div><div><div>3</div></div><div><span>mp4</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>10秒</span></div></div><div><div><div>6</div></div><div><span>https://www.runoob.com/try/demo_source/mov_bbb.mp4</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>10秒</span></div></div><div><div><div>9</div></div><div><span>https://www.runoob.com/try/demo_source/movie.mp4</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>1分钟</span></div></div><div><div><div>12</div></div><div><span>http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>46秒</span></div></div><div><div><div>15</div></div><div><span>http://vjs.zencdn.net/v/oceans.mp4</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>52秒</span></div></div><div><div><div>18</div></div><div><span>http://vjs.zencdn.net/v/oceans.mp4</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>小米 mix fold3</span></div></div><div><div><div>21</div></div><div><span>https://cdn.cnbj1.fds.api.mi-img.com/product-images/xiaomimixfold3k3zux1/video-1-1.mp4</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>图片上传接口url<a href="#图片上传接口url"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>none;</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>http://live-uat.nrbvc.com/chicken/main.flv</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>vscode 备忘录<a href="#vscode-备忘录"><span>#</span></a></h2><section><h3>代码注释折叠标志 #region #endregion<a href="#代码注释折叠标志-region-endregion"><span>#</span></a></h3><p>它可以让你把相关的代码折叠到一起,而无需和语法挂钩.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// #region xxx功能区域</span></div></div><div><div><div>2</div></div><div><span>// ...code</span></div></div><div><div><div>3</div></div><div><span>// #endregion</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/vue-yuan-ma-zhi-mustache-mu-ban/</id>
      <title type="text">vue源码之mustache模板</title>
      <published>2022-07-04T15:28:40.000Z</published>
      <updated>2022-07-04T15:28:40.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/vue-yuan-ma-zhi-mustache-mu-ban/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>什么是 mustache 模板<a href="#什么是-mustache-模板"><span>#</span></a></h2><p>mustache 模板是一种将数据转换为视图的技术!相比于<code>jq石器时代</code>拼接字符创,纯操作 dom 等方式,在当时这项技术是具有突破和轰动性的!为后来的模板引擎提供了很好的借鉴思路!从某种意义上来说,它是<code>vue编译</code>和<code>jsx</code>等技术的祖先!而<code>vue</code>使用的就是这种技术!</p></section>
<section><h2>简单的使用<a href="#简单的使用"><span>#</span></a></h2><p><a href="https://www.npmjs.com/package/mustache" target="_blank">NPM 地址</a>
也可以直接在项目中引用 cdn 来使用</p><p>下边我们来演示一下简单的使用,go!🏄</p><p>我们先准备好这样一个 html 文件,这里值得一提的是,<code>script</code>标签内的<code>text/template</code>类型其实是浏览器不认识的,我们正好利用这一特性来保存我们的模板 😁</p><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>"zh"</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>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>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;Document&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>script</span><span> </span><span>src</span><span>=</span><span>"./mustache.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>&lt;</span><span>script</span><span> </span><span>id</span><span>=</span><span>"template"</span><span> </span><span>type</span><span>=</span><span>"text/template"</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>&lt;</span><span>ul</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>{{#items}}</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>&lt;</span><span>li</span><span>&gt;{{.}}&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>{{/items}}</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>&lt;/</span><span>head</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>&lt;</span><span>body</span><span>&gt;</span></div></div><div><div><div>18</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>19</div></div><div><span><span>  </span></span><span>&lt;/</span><span>body</span><span>&gt;</span></div></div><div><div><div>20</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><p>然后就来到数据部分了,通过将数据和视图都输送给 mustache 这个库</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>items</span><span> </span><span>=</span><span> [</span><span>"a"</span><span>, </span><span>"b"</span><span>, </span><span>"c"</span><span>];</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span><span> { </span><span>items</span><span> };</span></span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>dom</span><span> </span><span>=</span><span><span> </span><span>mustache</span><span>.</span></span><span>render</span><span>(</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>document</span><span>.</span><span>getElementById</span><span>(</span><span>"template"</span><span>).</span><span>innerHTML</span><span>,</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></div></div><div><div><div>7</div></div><div><span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>"app"</span><span>).</span><span>innerHTML</span><span> </span><span>=</span><span><span> </span><span>dom</span><span>;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后我们就看到了这样的页面
</p><figure><img alt="页面最终渲染视图" loading="lazy" width="526" height="267" src="/_astro/20220704154410.BupnVA9q_f5FAm.webp" /><figcaption>页面最终渲染视图</figcaption></figure><p></p><p>wow,遥想当年疯狂拼接字符串的日子,出现这类技术,简直是内牛满面啊!😭</p><p>可以看到和 vue 的使用方式其实是差不多的,只是有一些差异,比如 for 循环的方式.</p></section>
<section><h2>这玩意是怎么实现的呢?<a href="#这玩意是怎么实现的呢"><span>#</span></a></h2><p>答案是拼接字符串.😁.’啊这?‘,莫着急,它的方式是好很多的.</p><section><h3>tokens<a href="#tokens"><span>#</span></a></h3><p>它里边有一个概念叫 tokens,它的作用是将模板分割成一块一块的,如果遇到嵌套的情况,就会形成多维数组,也就是形成嵌套的 tokens.然后将 tokens 转换为字符串,形成最终的字符串!虽然也是拼接字符串,但是体验可是好上一万倍啊!
</p><figure><img alt="tokens" loading="lazy" width="589" height="206" src="/_astro/mustachetokens.BEMzDTr3_eS5oD.webp" /><figcaption>tokens</figcaption></figure><p></p><p>就形如上边的字符串模板,mustache 会在碰到<code>{{}}</code>将模板分割,你可以理解为像数组的<code>split</code>方法一样,我们得到的 tokens 长度为 3,但是总的 tokens 的数量可不是 3,因为我们还有嵌套的 tokens(看图)!然后将 tokens 再结合我们给到的数据转换为<code>html</code>字符串,我们最终放到容器中,页面就渲染完成啦!😁</p></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>// 扫描类</span></div></div><div><div><div>2</div></div><div><span>class</span><span> </span><span>Scanner</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>templateStr</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>templateStr</span><span> </span><span>=</span><span><span> </span><span>templateStr</span><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>this</span><span>.</span><span>pos</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>tail</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></div></div><div><div><div>11</div></div><div><span>  </span><span>scan</span><span><span>(</span><span>str</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>pos</span><span> </span><span>+=</span><span><span> </span><span>str</span><span>.</span></span><span>length</span><span>;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>this</span><span>.</span><span>tail</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>templateStr</span><span>.</span><span>substring</span><span>(</span><span>this</span><span>.</span><span>pos</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></div></div><div><div><div>16</div></div><div><span>  </span><span>scanUtil</span><span><span>(</span><span>endFlag</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>const</span><span> </span><span>index</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>pos</span><span>;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>while</span><span> (</span><span>!</span><span>this</span><span>.</span><span>eos</span><span>() </span><span>&amp;&amp;</span><span> </span><span>!</span><span>this</span><span>.</span><span>tail</span><span>.</span><span>startsWith</span><span><span>(</span><span>endFlag</span><span>)) {</span></span></div></div><div><div><div>19</div></div><div><span>      </span><span>this</span><span>.</span><span>pos</span><span>++</span><span>;</span></div></div><div><div><div>20</div></div><div><span>      </span><span>this</span><span>.</span><span>tail</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>templateStr</span><span>.</span><span>substring</span><span>(</span><span>this</span><span>.</span><span>pos</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>const</span><span> </span><span>result</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>templateStr</span><span>.</span><span>substring</span><span><span>(</span><span>index</span><span>, </span></span><span>this</span><span>.</span><span>pos</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>scan</span><span><span>(</span><span>endFlag</span><span>);</span></span></div></div><div><div><div>24</div></div><div><span>    </span><span>return</span><span><span> </span><span>result</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></div></div><div><div><div>27</div></div><div><span>  </span><span>eos</span><span>() {</span></div></div><div><div><div>28</div></div><div><span>    </span><span>return</span><span> </span><span>this</span><span>.</span><span>pos</span><span> </span><span>&gt;=</span><span> </span><span>this</span><span>.</span><span>templateStr</span><span>.</span><span>length</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></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>// 将模板字符窜转换为tokens数组</span></div></div><div><div><div>33</div></div><div><span>const</span><span> </span><span>parseTemplateToTokens</span><span> </span><span>=</span><span><span> (</span><span>templateString</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>34</div></div><div><span>  </span><span>const</span><span> </span><span>tokens</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>35</div></div><div><span>  </span><span>const</span><span> </span><span>sc</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Scanner</span><span><span>(</span><span>templateString</span><span>);</span></span></div></div><div><div><div>36</div></div><div><span>  </span><span>let</span><span><span> </span><span>words</span><span>;</span></span></div></div><div><div><div>37</div></div><div><span>  </span><span>while</span><span> (</span><span>!</span><span><span>sc</span><span>.</span></span><span>eos</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></span><span>words</span><span> </span><span>=</span><span><span> </span><span>sc</span><span>.</span></span><span>scanUtil</span><span>(</span><span>"{{"</span><span>);</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>tokens</span><span>.</span><span>push</span><span>([</span><span>"text"</span><span><span>, </span><span>words</span><span>]);</span></span></div></div><div><div><div>41</div></div><div><span>    </span><span>// 寻找开始标记与结束标记之间的文字</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>words</span><span> </span><span>=</span><span><span> </span><span>sc</span><span>.</span></span><span>scanUtil</span><span>(</span><span>"}}"</span><span>);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>if</span><span><span> (</span><span>words</span><span> </span></span><span>!==</span><span> </span><span>""</span><span>) {</span></div></div><div><div><div>44</div></div><div><span>      </span><span>//  解析嵌套情况</span></div></div><div><div><div>45</div></div><div><span>      </span><span>if</span><span><span> (</span><span>words</span><span>[</span></span><span>0</span><span>] </span><span>===</span><span> </span><span>"#"</span><span>) {</span></div></div><div><div><div>46</div></div><div><span>        </span><span>//嵌套开始标记</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>tokens</span><span>.</span><span>push</span><span>([</span><span>"#"</span><span><span>, </span><span>words</span><span>.</span></span><span>substring</span><span>(</span><span>1</span><span>)]);</span></div></div><div><div><div>48</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>words</span><span>[</span></span><span>0</span><span>] </span><span>===</span><span> </span><span>"/"</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>        </span><span>//嵌套结束标记</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>tokens</span><span>.</span><span>push</span><span>([</span><span>"/"</span><span><span>, </span><span>words</span><span>.</span></span><span>substring</span><span>(</span><span>1</span><span>)]);</span></div></div><div><div><div>51</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>52</div></div><div><span>        </span><span>// 非嵌套</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>tokens</span><span>.</span><span>push</span><span>([</span><span>"name"</span><span><span>, </span><span>words</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>    </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>// 到这里tokens是打平的一维数组,我们需要将其折叠为多维数组来满足嵌套关系</span></div></div><div><div><div>58</div></div><div><span>  </span><span>return</span><span> </span><span>nestTokens</span><span><span>(</span><span>tokens</span><span>);</span></span></div></div><div><div><div>59</div></div><div><span>};</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>// 折叠嵌套的tokens</span></div></div><div><div><div>62</div></div><div><span>const</span><span> </span><span>nestTokens</span><span> </span><span>=</span><span><span> (</span><span>tokens</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>63</div></div><div><span>  </span><span>const</span><span> </span><span>nestedTokens</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>64</div></div><div><span>  </span><span>// 收集器,用来指向我们正在操作的元素的折叠项</span></div></div><div><div><div>65</div></div><div><span>  </span><span>let</span><span><span> </span><span>collector</span><span> </span></span><span>=</span><span><span> </span><span>nestedTokens</span><span>;</span></span></div></div><div><div><div>66</div></div><div><span>  </span><span>// 维护一个栈的数据结构,保证栈顶永远是我们正在操作的元素</span></div></div><div><div><div>67</div></div><div><span>  </span><span>const</span><span> </span><span>stack</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>68</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>tokens</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>69</div></div><div><span>    </span><span>const</span><span> </span><span>token</span><span> </span><span>=</span><span><span> </span><span>tokens</span><span>[</span><span>i</span><span>];</span></span></div></div><div><div><div>70</div></div><div><span>    </span><span>switch</span><span><span> (</span><span>token</span><span>[</span></span><span>0</span><span>]) {</span></div></div><div><div><div>71</div></div><div><span>      </span><span>case</span><span> </span><span>"#"</span><span>:</span></div></div><div><div><div>72</div></div><div><span><span>        </span></span><span>collector</span><span>.</span><span>push</span><span><span>(</span><span>token</span><span>);</span></span></div></div><div><div><div>73</div></div><div><span><span>        </span></span><span>stack</span><span>.</span><span>push</span><span><span>(</span><span>token</span><span>);</span></span></div></div><div><div><div>74</div></div><div><span>        </span><span>// 给token添加第三项数组项,并让收集器指向它</span></div></div><div><div><div>75</div></div><div><span><span>        </span></span><span>collector</span><span> </span><span>=</span><span><span> </span><span>token</span><span>[</span></span><span>2</span><span>] </span><span>=</span><span> [];</span></div></div><div><div><div>76</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>77</div></div><div><span>      </span><span>case</span><span> </span><span>"/"</span><span>:</span></div></div><div><div><div>78</div></div><div><span><span>        </span></span><span>stack</span><span>.</span><span>pop</span><span>();</span></div></div><div><div><div>79</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span><span>stack</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>)) {</span></div></div><div><div><div>80</div></div><div><span><span>          </span></span><span>collector</span><span> </span><span>=</span><span><span> </span><span>nestedTokens</span><span>;</span></span></div></div><div><div><div>81</div></div><div><span>          </span><span>break</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>        </span></span><span>collector</span><span> </span><span>=</span><span><span> </span><span>stack</span><span>.</span></span><span>at</span><span>(</span><span>-</span><span>1</span><span>)[</span><span>2</span><span>];</span></div></div><div><div><div>84</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>85</div></div><div><span>      </span><span>default</span><span>:</span></div></div><div><div><div>86</div></div><div><span><span>        </span></span><span>collector</span><span>.</span><span>push</span><span><span>(</span><span>token</span><span>);</span></span></div></div><div><div><div>87</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>88</div></div><div><span><span>    </span></span><span>}</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><span>  </span><span>return</span><span><span> </span><span>nestedTokens</span><span>;</span></span></div></div><div><div><div>91</div></div><div><span>};</span></div></div><div><div><div>92</div></div><div>
</div></div><div><div><div>93</div></div><div><span>// tokens render 单层级</span></div></div><div><div><div>94</div></div><div><span>const</span><span> </span><span>tokensRender</span><span> </span><span>=</span><span><span> (</span><span>tokens</span><span>, </span><span>data</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>95</div></div><div><span>  </span><span>let</span><span><span> </span><span>result</span><span> </span></span><span>=</span><span> </span><span>""</span><span>;</span></div></div><div><div><div>96</div></div><div><span><span>  </span></span><span>tokens</span><span>.</span><span>forEach</span><span><span>((</span><span>t</span><span>, </span><span>i</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>97</div></div><div><span>    </span><span>const</span><span> </span><span>type</span><span> </span><span>=</span><span><span> </span><span>t</span><span>[</span></span><span>0</span><span>];</span></div></div><div><div><div>98</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>t</span><span>);</span></span></div></div><div><div><div>99</div></div><div><span>    </span><span>if</span><span><span> (</span><span>type</span><span> </span></span><span>===</span><span> </span><span>"text"</span><span>) {</span></div></div><div><div><div>100</div></div><div><span>      </span><span>// 普通文本</span></div></div><div><div><div>101</div></div><div><span><span>      </span></span><span>result</span><span> </span><span>+=</span><span><span> </span><span>t</span><span>[</span></span><span>1</span><span>];</span></div></div><div><div><div>102</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>type</span><span> </span></span><span>===</span><span> </span><span>"name"</span><span>) {</span></div></div><div><div><div>103</div></div><div><span>      </span><span>// key value</span></div></div><div><div><div>104</div></div><div><span>      </span><span>const</span><span> </span><span>key</span><span> </span><span>=</span><span><span> </span><span>t</span><span>[</span></span><span>1</span><span>];</span></div></div><div><div><div>105</div></div><div><span>      </span><span>if</span><span><span> (</span><span>key</span><span> </span></span><span>===</span><span> </span><span>"."</span><span>) {</span></div></div><div><div><div>106</div></div><div><span><span>        </span></span><span>result</span><span> </span><span>+=</span><span><span> </span><span>data</span><span>;</span></span></div></div><div><div><div>107</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>108</div></div><div><span>        </span><span>// 解决连续键</span></div></div><div><div><div>109</div></div><div><span>        </span><span>const</span><span> </span><span>paths</span><span> </span><span>=</span><span><span> </span><span>key</span><span>.</span></span><span>split</span><span>(</span><span>"."</span><span>);</span></div></div><div><div><div>110</div></div><div><span>        </span><span>// 指针</span></div></div><div><div><div>111</div></div><div><span>        </span><span>let</span><span><span> </span><span>point</span><span> </span></span><span>=</span><span><span> </span><span>data</span><span>;</span></span></div></div><div><div><div>112</div></div><div><span><span>        </span></span><span>paths</span><span>.</span><span>forEach</span><span><span>((</span><span>path</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>113</div></div><div><span><span>          </span></span><span>point</span><span> </span><span>=</span><span><span> </span><span>point</span><span>[</span><span>path</span><span>];</span></span></div></div><div><div><div>114</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>115</div></div><div>
</div></div><div><div><div>116</div></div><div><span><span>        </span></span><span>result</span><span> </span><span>+=</span><span><span> </span><span>point</span><span>;</span></span></div></div><div><div><div>117</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>118</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>119</div></div><div><span>      </span><span>const</span><span> </span><span>key</span><span> </span><span>=</span><span><span> </span><span>t</span><span>[</span></span><span>1</span><span>];</span></div></div><div><div><div>120</div></div><div><span>      </span><span>const</span><span> </span><span>nestTokens</span><span> </span><span>=</span><span><span> </span><span>t</span><span>[</span></span><span>2</span><span>];</span></div></div><div><div><div>121</div></div><div><span>      </span><span>const</span><span> </span><span>nestData</span><span> </span><span>=</span><span><span> </span><span>data</span><span>[</span><span>key</span><span>];</span></span></div></div><div><div><div>122</div></div><div><span>      </span><span>const</span><span> </span><span>str</span><span> </span><span>=</span><span> </span><span>parseArrayTokens</span><span><span>(</span><span>nestTokens</span><span>, </span><span>nestData</span><span>);</span></span></div></div><div><div><div>123</div></div><div><span><span>      </span></span><span>result</span><span> </span><span>+=</span><span><span> </span><span>str</span><span>;</span></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></span><span>});</span></div></div><div><div><div>126</div></div><div><span>  </span><span>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>127</div></div><div><span>};</span></div></div><div><div><div>128</div></div><div>
</div></div><div><div><div>129</div></div><div><span>// 解析嵌套tokens</span></div></div><div><div><div>130</div></div><div><span>const</span><span> </span><span>parseArrayTokens</span><span> </span><span>=</span><span><span> (</span><span>toenks</span><span>, </span><span>array</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>131</div></div><div><span>  </span><span>let</span><span><span> </span><span>result</span><span> </span></span><span>=</span><span> </span><span>""</span><span>;</span></div></div><div><div><div>132</div></div><div><span><span>  </span></span><span>array</span><span>.</span><span>forEach</span><span><span>((</span><span>node</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>133</div></div><div><span>    </span><span>// 这里我们循环交替调用,形成一个递归效果</span></div></div><div><div><div>134</div></div><div><span><span>    </span></span><span>result</span><span> </span><span>+=</span><span> </span><span>tokensRender</span><span><span>(</span><span>toenks</span><span>, </span><span>node</span><span>);</span></span></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>return</span><span><span> </span><span>result</span><span>;</span></span></div></div><div><div><div>137</div></div><div><span>};</span></div></div><div><div><div>138</div></div><div>
</div></div><div><div><div>139</div></div><div><span>const</span><span> </span><span>pandaTemplate</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>140</div></div><div><span>  </span><span>render</span><span><span>(</span><span>template</span><span>, </span><span>data</span><span>) {</span></span></div></div><div><div><div>141</div></div><div><span>    </span><span>const</span><span> </span><span>tokens</span><span> </span><span>=</span><span> </span><span>parseTemplateToTokens</span><span><span>(</span><span>template</span><span>);</span></span></div></div><div><div><div>142</div></div><div><span>    </span><span>const</span><span> </span><span>htmlStr</span><span> </span><span>=</span><span> </span><span>tokensRender</span><span><span>(</span><span>tokens</span><span>, </span><span>data</span><span>);</span></span></div></div><div><div><div>143</div></div><div><span>    </span><span>return</span><span><span> </span><span>htmlStr</span><span>;</span></span></div></div><div><div><div>144</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>145</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>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>2</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><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>const</span><span> </span><span>data</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>name</span><span>:</span><span> </span><span>"panda"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>age</span><span>:</span><span> </span><span>18</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>info</span><span>:</span><span> {</span></div></div><div><div><div>9</div></div><div><span>      </span><span>pc</span><span>:</span><span> {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>type</span><span>:</span><span> </span><span>"macbookpromaxplusUltraman"</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><span>    </span><span>cars</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> </span><span>"劳斯奈斯幻影"</span><span>,</span></div></div><div><div><div>16</div></div><div><span>        </span><span>keys</span><span>:</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>girls</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>name</span><span>:</span><span> </span><span>"小红"</span><span>,</span></div></div><div><div><div>22</div></div><div><span>        </span><span>children</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>name</span><span>:</span><span> </span><span>"孩子一"</span><span>,</span></div></div><div><div><div>25</div></div><div><span>            </span><span>age</span><span>:</span><span> </span><span>8</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>name</span><span>:</span><span> </span><span>"孩子二"</span><span>,</span></div></div><div><div><div>29</div></div><div><span>            </span><span>age</span><span>:</span><span> </span><span>9</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><span>name</span><span>:</span><span> </span><span>"孩子三"</span><span>,</span></div></div><div><div><div>33</div></div><div><span>            </span><span>age</span><span>:</span><span> </span><span>10</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><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>const</span><span> </span><span>htmlStr</span><span> </span><span>=</span><span><span> </span><span>pandaTemplate</span><span>.</span></span><span>render</span><span>(</span></div></div><div><div><div>40</div></div><div><span>    </span><span>`</span></div></div><div><div><div>41</div></div><div><span><span>      </span></span><span>&lt;div&gt;</span></div></div><div><div><div>42</div></div><div><span><span>      </span></span><span>&lt;p&gt;姓名:{{name}},&lt;/p&gt;</span></div></div><div><div><div>43</div></div><div><span><span>      </span></span><span>&lt;p&gt;年龄:{{age}}&lt;/p&gt;</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>&lt;ul&gt;</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>{{#cars}}</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>&lt;li&gt;{{name}},钥匙:[ {{#keys}} {{.}}, {{/key}} ]&lt;/li&gt;</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>{{/cars}}</span></div></div><div><div><div>49</div></div><div><span><span>      </span></span><span>&lt;/ul&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>&lt;ul&gt;</span></div></div><div><div><div>52</div></div><div><span><span>        </span></span><span>{{#girls}}</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>&lt;li&gt;</span></div></div><div><div><div>54</div></div><div><span><span>          </span></span><span>{{name}},现有小孩:[ {{#children}}&lt;sapn&gt;{{name}},&lt;/sapn&gt; {{/children}} ]</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>&lt;/li&gt;</span></div></div><div><div><div>56</div></div><div><span><span>        </span></span><span>{{/girls}}</span></div></div><div><div><div>57</div></div><div><span><span>      </span></span><span>&lt;/ul&gt;</span></div></div><div><div><div>58</div></div><div><span><span>      </span></span><span>&lt;p&gt;其他信息:{{info.pc.type}}&lt;/p&gt;</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>60</div></div><div><span><span>                  </span></span><span>`</span><span>,</span></div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>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>
</div></div><div><div><div>64</div></div><div><span><span>  </span></span><span>document</span><span>.</span><span>getElementById</span><span>(</span><span>"app"</span><span>).</span><span>innerH</span><span>;</span></div></div><div><div><div>65</div></div><div><span>  </span><span>TML</span><span> </span><span>=</span><span><span> </span><span>htmlStr</span><span>;</span></span></div></div><div><div><div>66</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><p></p><figure><img alt="tokens" loading="lazy" width="612" height="492" src="/_astro/20220708114253.JBITSJ4T_2hWyi1.webp" /><figcaption>tokens</figcaption></figure><p></p><p>ok,这样就基本完成啦!可以在每个函数的关键部分打印一下,看看每个函数的作用,这样就能很快整个流程和实现细节啦~</p></section></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>没有什么好说的,加油!</p></section>]]></content>
    </entry>
    <entry>
      <id>https://slothvips.github.io/posts/wo-mian-fei-le-li-zhi-you-gan/</id>
      <title type="text">我免费了！（🌼🐓）--离职有感</title>
      <published>2022-06-21T00:00:00.000Z</published>
      <updated>2022-06-21T00:00:00.000Z</updated>
      <author><name>panda</name></author>
      <link rel="alternate" href="https://slothvips.github.io/posts/wo-mian-fei-le-li-zhi-you-gan/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>我自由了！（I‘m free!）<a href="#我自由了im-free"><span>#</span></a></h2><p>是的，我从｀薯片科技｀这家公司离职了！要说有什么感觉,感觉很平淡,没有很多的感触,本来也是嘛,一切看起来都很正常.周一办完手续就回家了，今天算是第二天吧．想着可以做一些事情和安排，但是事与愿违，拖延症又犯咯，昨天基本一事无成！不能继续这样下去啊，虽然我是想玩半个月来着的，顺便去驾校转一转．但是要是想着要把生活打理的井井有条才行，我可不想每天晚上一趟上床来句：＇哎！今天又啥都没干！＇</p><p>尽管这家公司的制度让人难受，而且算是比较坑的那种吧，但是你到任何一家公司都会有收获．👴 今天就来总结整理一下吧！</p></section>
<section><h2>学到的东西<a href="#学到的东西"><span>#</span></a></h2><p>其实收获还是挺多的，比如多人协作，因为我以前都没去过这种规模的公司，对这些都还是比较陌生的．在这个公司，你基本天天需要和部门外的人打交道，多人协作的能力就显得格外重要了！其次是架构方面的东西吧，在以前我做的东西和外部应用基本是没有什么关联的，这家公司很多产品之间多多少少都会有一些互动，也算是熟悉了构建一个企业级应用框架的模型吧．</p><section><h3>技术层面 🔧<a href="#技术层面-"><span>#</span></a></h3><p>这方面的话，只有架构层面的东西让我觉得学到了！其他的话，多是常规的业务代码，虽然也能学习到一些东西，但是差强人意吧．</p><p>相比于以前接触到的传统项目，他们把每个功能模块进行了拆分，成为了独立的模块，这样做的好处是迭代方便，各模块之间耦合性不高，将来很容易就能自由组合，甚至渐进式的重构，而不用担心影响全局！然而，这些和我们前端关系并不大，我们接触到的是中间层．公司有个部门叫｀数据中台｀，他们发挥了重要的作用，他们将数据＋接口组合来形成新的接口，方便我们的中间层调度．这个时候，前端才能通过中间层接触到后台的数据！</p><p>我当时走的时候，公司的前端架构似乎再往｀微前端｀方向走．这个坑应该比较多，我觉得其实就目前公司的情况，这样做只会吃力不讨好，因为存在太多问题了．确实，更多的是｀政治因素｀ 🐒．也就是所谓的｀ｋｐｉ｀行为，哈哈</p></section><section><h3>为人处世 ☕<a href="#为人处世-"><span>#</span></a></h3><p>这个也算是学到的一个东西，最大的收获是我对工作上的对接不再像以前那样恐惧了！实话说，这里的同事真的都挺好的，尽管公司很｀糟心｀．我来这里后最大的变化就是，和谁说话我都喊大佬 😄，发现这样似乎工作更舒心．我也放下了一些自己的高傲吧（虽然我不知道自己ＴＭ有时候在装啥！），能够与大部分同事愉快合作了！到目前为止，我还是挺抵触接触人的，但只是不再那么恐惧．（来自社交恐惧分子的无奈 😔）</p></section><section><h3>小节总结<a href="#小节总结"><span>#</span></a></h3><p>不管开心也好，还是快乐也好（这他妈不一个意思吗？＜.＜　离职当然开心啦），这个公司也算到告一段落了．谢谢接触到的人吧，毕竟我觉得我还算挺难相处的，哈哈哈！</p><p>说声３Ｑ，尽管你们看不到！</p></section></section>
<section><h2>接下来的计划<a href="#接下来的计划"><span>#</span></a></h2><p>两件事：</p><ul>
<li>科目二(报了快一个月了,一点进度都没有 💀)</li>
<li>瑞拉克丝,放送一波</li>
</ul><p>另外,由于闲着没事干,用<code>vuepress</code>替换了’hexo’,这个博客就诞生啦!感觉比’hexo’好用很多,哈哈;只是以前的文档就懒得迁移咯,封存吧.</p><p>好的,没啥说的了,继续<code>冲</code>吧 🤬!</p></section>]]></content>
    </entry>
</feed>
