<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Posts on Xiaohan&#39;s Blog</title>
		<link>https://yunbo.li/posts/</link>
		<description>Recent content in Posts on Xiaohan&#39;s Blog</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>zh-cn</language>
		<lastBuildDate>Sun, 10 Feb 2019 21:01:24 +0800</lastBuildDate>
		<atom:link href="https://yunbo.li/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>模拟实现Promise(下)</title>
			<link>https://yunbo.li/posts/homemade-promise-part2/</link>
			<pubDate>Sun, 10 Feb 2019 21:01:24 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/homemade-promise-part2/</guid>
			<description>&lt;p&gt;上篇文章中我们实现了一个具有基础功能的&lt;code&gt;MyPromise&lt;/code&gt;，除了内部使用的&lt;code&gt;_resolve&lt;/code&gt;和&lt;code&gt;_reject&lt;/code&gt;外，我们只实现了&lt;code&gt;Promise.prototype.then&lt;/code&gt;，那么这篇文章中，我们来实现更多 API。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>上篇文章中我们实现了一个具有基础功能的<code>MyPromise</code>，除了内部使用的<code>_resolve</code>和<code>_reject</code>外，我们只实现了<code>Promise.prototype.then</code>，那么这篇文章中，我们来实现更多 API。</p>
<h2 id="promiseprototypecatch">Promise.prototype.catch</h2>
<p><code>catch</code>的参数是<code>onRejected</code>，即拒绝事件的回调，它返回一个新的 Promise。如果当前 Promise 是决议状态的话，则决议值会透传给新的 Promise 上注册的决议事件回调。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">catch</span>(<span style="color:#a6e22e">onRejected</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">then</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">onRejected</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>它的实现利用了<code>.then</code>只能接收函数类型参数的特性，显式将<code>onFulfilled</code>设置为<code>null</code>。</p>
<h2 id="promiseprototypefinally">Promise.prototype.finally</h2>
<p>这是一个 ES2018 的新特性。<code>finally</code>方法返回一个 Promise，它接收的参数称为<code>onFinally</code>，其特点是，无论当前 Promise 被决议或者是被拒绝，<code>onFinally</code>都会执行。这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。需要注意的是，只有在<code>onFinally</code>中 throw 或返回被拒绝的 Promise 时，<code>onFinally</code>才会返回被相应原因拒绝的 Promise，否则返回的 Promise 状态与上一个 Promise 保持一致。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">finally</span>(<span style="color:#a6e22e">cb</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">then</span>(
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">value</span> =&gt; <span style="color:#a6e22e">MyPromise</span>.<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">cb</span>()).<span style="color:#a6e22e">then</span>(() =&gt; <span style="color:#a6e22e">value</span>),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">reason</span> =&gt;
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">MyPromise</span>.<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">cb</span>()).<span style="color:#a6e22e">then</span>(() =&gt; {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">reason</span>;
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="promisereject">Promise.reject</h2>
<p>实例上的方法实现完之后，我们来实现 Promise 上的静态方法。<code>Promise.reject</code>返回一个拒绝状态的 Promise</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">value</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyPromise</span>((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">value</span>));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="promiseresolve">Promise.resolve</h2>
<p>与<code>Promise.reject</code>类似，<code>Promise.resolve</code>返回一个被决议的 Promise。但是后者多了一些细节，即如果传参是 Promise 实例的话，则直接返回这个实例。因此<code>Promise.resolve</code>常被用来保障某个值是 Promise。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">value</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">value</span> <span style="color:#66d9ef">instanceof</span> <span style="color:#a6e22e">MyPromise</span>) <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyPromise</span>(<span style="color:#a6e22e">resolve</span> =&gt; <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">value</span>));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="promiseall">Promise.all</h2>
<p><code>Promise.all</code>接收一个数组作为参数，参数数组中的成员可以是 Promise，也可以是普通值。它的返回值是一个 Promise，该返回值 Promise 被决议时表示参数数组中所有成员均被决议，此时返回值 Promise 决议值为一个数组，与参数数组的决议值一一对应；该返回值 Promise 被拒绝时，说明参数数组中有成员被拒绝，此时返回值 Promise 的拒绝原因即为那个被拒绝的成员的拒绝原因。</p>
<p><code>Promise.all</code>有一个特殊情况，当传递空数组时，返回决议值为空数组的 Promise。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">all</span>(<span style="color:#a6e22e">promises</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">promises</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">===</span> <span style="color:#ae81ff">0</span>) <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">MyPromise</span>.<span style="color:#a6e22e">resolve</span>([]);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyPromise</span>((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">resolvedValues</span> <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">resolvedCount</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">let</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">promises</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">MyPromise</span>.<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">promises</span>[<span style="color:#a6e22e">i</span>]).<span style="color:#a6e22e">then</span>(
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">value</span> =&gt; {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">resolvedValues</span>[<span style="color:#a6e22e">i</span>] <span style="color:#f92672">=</span> <span style="color:#a6e22e">value</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> (<span style="color:#f92672">++</span><span style="color:#a6e22e">resolvedCount</span> <span style="color:#f92672">===</span> <span style="color:#a6e22e">promises</span>.<span style="color:#a6e22e">length</span>) {
</span></span><span style="display:flex;"><span>              <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">resolvedValues</span>);
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">reason</span> =&gt; <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">reason</span>)
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="promisereject-1">Promise.reject</h2>
<p>传参形式与<code>Promise.all</code>相同，该函数的返回值是一个 Promise，当返回值 Promise 被决议时，说明参数数组中有成员被决议了，返回值的决议值即为该成员的决议值；当返回值 Promise 被拒绝时，说明参数数组中有成员被拒绝了，返回值的拒绝原因即为该成员的拒绝原因。</p>
<p>向<code>Promise.race</code>传递空数组时，返回值 Promise 会一直处于 Pending 状态，永远不会被决议或者拒绝，我们要避免这种情况。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">race</span>(<span style="color:#a6e22e">promises</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyPromise</span>((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">let</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">promises</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">MyPromise</span>.<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">promises</span>[<span style="color:#a6e22e">i</span>]).<span style="color:#a6e22e">then</span>(
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">value</span> =&gt; <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">value</span>),
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">reason</span> =&gt; <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">reason</span>)
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="promiseallsettled">Promise.allSettled</h2>
<p>这同样是一个 ES2018 新增的 API。它的传参与<code>Promise.all</code>一致，但返回值 Promise 一定会被决议，且决议值同样是数组，数组成员的形式要么是<code>{status: 'fulfilled', value: FulfilledValue}</code>，要么是<code>{status: 'rejected', reason: RejectedReason}</code>，具体是哪一种，取决于对应位置上的参数数组成员被决议还是拒绝。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#a6e22e">allSettled</span>(<span style="color:#a6e22e">promises</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Promise.<span style="color:#a6e22e">all</span>(
</span></span><span style="display:flex;"><span>      Array.<span style="color:#a6e22e">from</span>(<span style="color:#a6e22e">promises</span>, <span style="color:#a6e22e">p</span> =&gt; {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Promise.<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">p</span>).<span style="color:#a6e22e">then</span>(
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">value</span> =&gt; ({ <span style="color:#a6e22e">status</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;fulfilled&#34;</span>, <span style="color:#a6e22e">value</span> }),
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">reason</span> =&gt; ({ <span style="color:#a6e22e">status</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;rejected&#34;</span>, <span style="color:#a6e22e">reason</span> })
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>      })
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>模拟实现Promise(上)</title>
			<link>https://yunbo.li/posts/homemade-promise-part1/</link>
			<pubDate>Sun, 16 Dec 2018 19:29:01 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/homemade-promise-part1/</guid>
			<description>&lt;p&gt;JavaScript 中的异步问题一直都让人苦恼，直到有了 Promise。Promise 是一种对未来值的封装，是一种许诺，类似于“我承诺将来会给你一个值”。用了这么久 Promise，对基本 API 的使用已经很熟练了，每次用的时候都感觉 Promise 就像魔法一般神奇。那么 Promise 是如何实现这种魔法的呢？&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>JavaScript 中的异步问题一直都让人苦恼，直到有了 Promise。Promise 是一种对未来值的封装，是一种许诺，类似于“我承诺将来会给你一个值”。用了这么久 Promise，对基本 API 的使用已经很熟练了，每次用的时候都感觉 Promise 就像魔法一般神奇。那么 Promise 是如何实现这种魔法的呢？</p>
<p>本篇文章将从头实现一个<code>MyPromise</code>，它和 ES6 Promise 具有一样的 API。同时，为了书写方便，我们将借助 ES6 Class 来完成编码。</p>
<h2 id="构造函数">构造函数</h2>
<p>首先我们知道 Promise 有三种状态，分别是<code>pending</code>、<code>fulfilled</code>和<code>rejected</code>，并且状态改变只能由<code>pending</code>向其他两种状态单向转移。同时，我们需要维护一个值用来存储 Promise 的决议值或者拒绝原因。Promise 的构造函数接收一个参数，名为<code>executor</code>，它是一个函数，接收 Promise 传给它的<code>resolve</code>和<code>reject</code>两个函数作为参数。Promise 构造出来之后，我们可以调用 Promise 实例的<code>.then</code>方法来注册决议或拒绝的事件监听函数，并且是可以注册多个的。结合上面这些信息，我们可以先搭一个框架出来。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#75715e">// Promise的三种状态
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">PENDING</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;PENDING&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">FULFILLED</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;FULFILLED&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">REJECTED</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;REJECTED&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 工具函数，判断参数是否为函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">isFunc</span>(<span style="color:#a6e22e">v</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">v</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;function&#34;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">constructor</span>(<span style="color:#a6e22e">executor</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 所处状态
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">PENDING</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 决议值，可能是完成值，也可能是拒绝原因
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// onFulfilled事件回调队列
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_fulfilledQueue</span> <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// onRejected事件回调队列
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_rejectedQueue</span> <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 执行executor，如果发生错误
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">// 则将当前Promise设置为拒绝状态
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">executor</span>(<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_resolve</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#66d9ef">this</span>), <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_reject</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#66d9ef">this</span>));
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">err</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_reject</span>(<span style="color:#a6e22e">err</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_resolve</span>() {}
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_reject</span>() {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>首先把三种状态抽象成变量方便以后使用，然后我们声明了一个工具函数<code>isFunc</code>，用来判断参数是否是函数。在构造函数内，除了存储状态的变量和存储决议值或拒绝原因的变量外，还需要声明两个队列，用来存储 Promise 决议或拒绝前，被注册的事件回调。目前<code>this._reject</code>和<code>this._resolve</code>还没有被实现，不要急，接着往下看。</p>
<h2 id="_reject">_reject</h2>
<p><code>_reject</code>执行时，Promise 应处于<code>pending</code>状态，否则说明 Promise 已经决议或拒绝。如果状态没问题的话，则设置当前 Promise 的状态为<code>rejected</code>，同时把拒绝原因存储起来，然后从拒绝事件回调队列中取出所有回调一一执行。我们来按照这个思路实现一下。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#a6e22e">_reject</span>(<span style="color:#a6e22e">reason</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 保证Promise的状态转换只能发生一次
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">!==</span> <span style="color:#a6e22e">PENDING</span>) <span style="color:#66d9ef">return</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">run</span> <span style="color:#f92672">=</span> () =&gt; {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">reason</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">REJECTED</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// 从相应队列里取出所有回调函数并执行
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">cb</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">while</span> ((<span style="color:#a6e22e">cb</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_rejectedQueue</span>.<span style="color:#a6e22e">shift</span>())) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">cb</span>(<span style="color:#a6e22e">reason</span>);
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 为了支持&#34;同步&#34;的Promise
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">// 这里使用setTimeout来异步执行run
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#a6e22e">setTimeout</span>(<span style="color:#a6e22e">run</span>, <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>注意到我们把执行回调函数的过程封装在了<code>setTimeout</code>中，这是为什么呢？直接同步执行这个逻辑可不可以呢？考虑下面的代码</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Promise((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">reject</span>();
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">then</span>(<span style="color:#66d9ef">null</span>, () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;p is rejected&#34;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Promise 并不止被用于异步过程，它的构造函数完全可以不包含任何异步过程。在上面的代码中，我们构造 Promise 的同时直接将其同步拒绝了，而且此时还没有执行到<code>p.then</code>这里，也就是说还没有注册上拒绝事件回调。因此从事件回调队列中取回调函数并一一执行的过程需要是异步的，这样才能保证像上面这段代码一样的“同步”Promise 能够正常执行。</p>
<h2 id="_resolve">_resolve</h2>
<p>与<code>_reject</code>类似，<code>_reject</code>首先要保证状态转义符合要求，同时取出回调事件执行。但比<code>_reject</code>更强大的是，如果传参(也就是决议值)是 Promise 实例的话，<code>_resolve</code>则会跟踪自己的传参，因此决议值会变成传参的决议值而不是传参本身。举个例子</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Promise((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">resolve</span>(Promise.<span style="color:#a6e22e">resolve</span>(<span style="color:#ae81ff">42</span>));
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">value</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;p is resolved with&#34;</span>, <span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#75715e">// -&gt; p is resolved with 42
</span></span></span></code></pre></div><p>接下来我们来看一下如何实现这个神奇的特性</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#a6e22e">_resolve</span>(<span style="color:#a6e22e">value</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">!==</span> <span style="color:#a6e22e">PENDING</span>) <span style="color:#66d9ef">return</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">run</span> <span style="color:#f92672">=</span> () =&gt; {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">runFulfilled</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">value</span> =&gt; {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">cb</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">while</span> ((<span style="color:#a6e22e">cb</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_fulfilledQueue</span>.<span style="color:#a6e22e">shift</span>())) {
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">cb</span>(<span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">runRejected</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">reason</span> =&gt; {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">cb</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">while</span> ((<span style="color:#a6e22e">cb</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_rejectedQueue</span>.<span style="color:#a6e22e">shift</span>())) {
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">cb</span>(<span style="color:#a6e22e">reason</span>);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">value</span> <span style="color:#66d9ef">instanceof</span> <span style="color:#a6e22e">MyPromise</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">then</span>(
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">v</span> =&gt; {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">v</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">FULFILLED</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">runFulfilled</span>(<span style="color:#a6e22e">v</span>);
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">r</span> =&gt; {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">r</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">REJECTED</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">runRejected</span>(<span style="color:#a6e22e">r</span>);
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">value</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">FULFILLED</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">runFulfilled</span>(<span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">setTimeout</span>(<span style="color:#a6e22e">run</span>, <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>整体结构上和<code>_reject</code>类似，但是<code>_resolve</code>中多了一个用来判断传参类型的逻辑，即传参如果是 Promise 实例，则在传参上注册<code>.then</code>事件，然后根据传参最终被决议还是拒绝来决定当前 Promise 应该被决议还是拒绝。最后，依然是由于“同步”Promise 的问题，需要把整个过程用<code>setTimeout</code>包裹起来，确保逻辑执行时已经被注册了事件回调。</p>
<h2 id="then">then</h2>
<p>根据规范，<code>then</code>位于 Promise 的实例上，每次调用都会返回一个新的 Promise 实例。它的作用是注册事件回调，决议事件回调一般被称为<code>onFulfilled</code>，拒绝事件回调一般被称为<code>onRejected</code>。<code>then</code>也有一个神奇的特性，如果<code>onFulfilled</code>或者<code>onRejected</code>返回了 Promise 实例，则<code>then</code>返回的新 Promise 实例会跟踪这些返回值 Promise。如下面的例子所示。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Promise((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">resolve</span>(Promise.<span style="color:#a6e22e">resolve</span>());
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p1</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">then</span>(() =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Promise.<span style="color:#a6e22e">resolve</span>(<span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">value</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;p1 is resolved with&#34;</span>, <span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#75715e">// -&gt; p1 is resolved with 42
</span></span></span></code></pre></div><p>具体实现如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyPromise</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">onFulfilled</span>, <span style="color:#a6e22e">onRejected</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyPromise</span>((<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>) =&gt; {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">fulfilled</span>(<span style="color:#a6e22e">value</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">isFunc</span>(<span style="color:#a6e22e">onFulfilled</span>)) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>          } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">ret</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">onFulfilled</span>(<span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">ret</span> <span style="color:#66d9ef">instanceof</span> <span style="color:#a6e22e">MyPromise</span>) {
</span></span><span style="display:flex;"><span>              <span style="color:#a6e22e">ret</span>.<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>);
</span></span><span style="display:flex;"><span>            } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>              <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">ret</span>);
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">err</span>) {
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">err</span>);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">rejected</span>(<span style="color:#a6e22e">reason</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">isFunc</span>(<span style="color:#a6e22e">onRejected</span>)) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">reason</span>);
</span></span><span style="display:flex;"><span>          } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">ret</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">onRejected</span>(<span style="color:#a6e22e">reason</span>);
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">ret</span> <span style="color:#66d9ef">instanceof</span> <span style="color:#a6e22e">MyPromise</span>) {
</span></span><span style="display:flex;"><span>              <span style="color:#a6e22e">ret</span>.<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">resolve</span>, <span style="color:#a6e22e">reject</span>);
</span></span><span style="display:flex;"><span>            } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>              <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">ret</span>);
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">err</span>) {
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">reject</span>(<span style="color:#a6e22e">err</span>);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">switch</span> (<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_status</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">PENDING</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_fulfilledQueue</span>.<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">onFulfilled</span>);
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_rejectedQueue</span>.<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">onRejected</span>);
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">FULFILLED</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">fulfilled</span>(<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span>);
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">REJECTED</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">rejected</span>(<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_value</span>);
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>可以看到总体结构是这样的，首先无脑构造一个 Promise 实例并返回，在该 Promise 实例的构造函数中，首先判断一下当前 Promise 的状态，如果还没被决议或拒绝，则先将事件回调存储在队列中。否则的话，直接调用相应的事件回调函数，判断返回值是否是 Promise 实例，如果是的话则追踪，不是的话新 Promise 将用该返回值决议或拒绝。</p>
<p>其中用到了我们之前声明的<code>isFunc</code>工具函数，因为在 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then">MDN</a> 中有这样一段描述</p>
<blockquote>
<p>注意：如果忽略针对某个状态的回调函数参数，或者提供非函数 (nonfunction) 参数，那么 then 方法将会丢失关于该状态的回调函数信息，但是并不会产生错误。如果调用 then 的 Promise 的状态（fulfillment 或 rejection）发生改变，但是 then 中并没有关于这种状态的回调函数，那么 then 将创建一个没有经过回调函数处理的新 Promise 对象，这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。</p></blockquote>
<p>什么意思呢，我们还是举个例子来说明吧</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Promise(<span style="color:#a6e22e">resolve</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">resolve</span>(<span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">then</span>(<span style="color:#66d9ef">null</span>, () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;I will never run&#34;</span>);
</span></span><span style="display:flex;"><span>}).<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">value</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;p.then() is resolved with&#34;</span>, <span style="color:#a6e22e">value</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#75715e">// -&gt; p.then() is resolved with 42
</span></span></span></code></pre></div><p>相应回调函数缺失或者不是函数时，效果就像决议值或拒绝原因被“透传”给<code>.then</code>返回的 Promise 的相应回调函数了。</p>
<h2 id="总结">总结</h2>
<p>至此，我们就算模拟实现了一个比较完善的基础 Promise。实现过程中，我们可以发现看似神奇的 Promise 背后其实并没有魔法，它本质上还是基于回调实现的。目前我们实现的这个 Promise 只具有一些基本功能，而剩下的一些 API，如<code>Promise.prototype.catch</code>、<code>Promise.prototype.finally</code>、<code>Promise.all</code>等，留到下一篇再详细介绍吧~</p>
]]></content>
		</item>
		
		<item>
			<title>浏览器缓存机制</title>
			<link>https://yunbo.li/posts/cache-experiment/</link>
			<pubDate>Sun, 06 May 2018 17:21:30 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/cache-experiment/</guid>
			<description>&lt;h3 id=&#34;基本流程&#34;&gt;基本流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;加载资源时，首先根据上次资源响应 Header 中的&lt;code&gt;Expires&lt;/code&gt;和&lt;code&gt;Cache-Control&lt;/code&gt;判断是否命中强缓存，如果命中则直接从缓存中取，不会发送资源请求到服务器&lt;/li&gt;
&lt;li&gt;如果没有命中强缓存，则服务端根据&lt;code&gt;Last-Modified/If-Modified-Since&lt;/code&gt;和&lt;code&gt;ETag/If-None-Match&lt;/code&gt;验证是否命中协商缓存，如果命中则返回 304 响应，不会返回资源数据&lt;/li&gt;
&lt;li&gt;如果没有命中协商缓存，则服务端返回资源数据&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;比较&#34;&gt;比较&lt;/h3&gt;
&lt;p&gt;相同点：不论是哪种缓存，都是从本地加载资源，服务端不会返回资源数据&lt;!-- raw HTML omitted --&gt;不同点：强缓存不会产生请求，协商缓存会产生请求&lt;/p&gt;</description>
			<content type="html"><![CDATA[<h3 id="基本流程">基本流程</h3>
<ol>
<li>加载资源时，首先根据上次资源响应 Header 中的<code>Expires</code>和<code>Cache-Control</code>判断是否命中强缓存，如果命中则直接从缓存中取，不会发送资源请求到服务器</li>
<li>如果没有命中强缓存，则服务端根据<code>Last-Modified/If-Modified-Since</code>和<code>ETag/If-None-Match</code>验证是否命中协商缓存，如果命中则返回 304 响应，不会返回资源数据</li>
<li>如果没有命中协商缓存，则服务端返回资源数据</li>
</ol>
<h3 id="比较">比较</h3>
<p>相同点：不论是哪种缓存，都是从本地加载资源，服务端不会返回资源数据<!-- raw HTML omitted -->不同点：强缓存不会产生请求，协商缓存会产生请求</p>
<h3 id="强缓存">强缓存</h3>
<p><code>Expires</code>表示缓存到期时间，是一个绝对时间，形如<code>Thu, 10 Oct 2017 01:47:03 GMT</code>，它存在于资源的响应头中。该字段出现于 HTTP/1.0 规范中。<!-- raw HTML omitted --><code>Cache-Control</code>表示缓存到期的<strong>相对时间</strong>，以下是一些常用字段值</p>
<ol>
<li>max-age=86400，表示 24 小时后失效</li>
<li>must-revalidate，如果超过了 max-age 设置的时间，必须发送请求验证资源是否还有效</li>
<li>no-cache，依然缓存资源，但是否使用该资源，由后续的对比决定</li>
<li>no-store，真正意义上的“不要缓存”</li>
<li>public，内容可以被客户端和代理缓存（如 CDN）</li>
<li>private，内容只能被客户端缓存，代理不能缓存，默认值。</li>
</ol>
<p>这些字段可以被混合使用，字段间关系如下<!-- raw HTML omitted --><img src="https://cdn.nlark.com/yuque/0/2019/webp/202126/1570675846312-5f287bdd-f63f-464f-b76b-b8f3760f6f0f.webp" alt="cache.webp"><!-- raw HTML omitted --><strong>Cache-Control 的优先级高于 Expires</strong>，出于兼容性考虑，两个字段常常会被同时设置</p>
<h3 id="协商缓存">协商缓存</h3>
<p>如果没有强缓存相关字段，或者强缓存判定资源失效时，进入协商缓存阶段机制(也就是说，<strong>强缓存优先级高于协商缓存</strong>)。</p>
<h4 id="last-modified--if-modified-since">Last-Modified &amp; If-Modified-Since</h4>
<p>第一次请求资源时，资源响应头通过<code>Last-Modified</code>字段告知客户端资源的修改时间，客户端会记录下该时间，在下次请求时，会将该时间写入资源请求头的<code>If-Modified-Since</code>字段。服务端收到该请求后，将<code>If-Modified-Since</code>中的时间与资源的上次修改时间进行对比，两个值一致时，返回 304 响应，否则正常返回新的资源。</p>
<h4 id="etag--if-none-match">ETag &amp; If-None-Match</h4>
<p>该机制的运行流程和<code>Last-Modified &amp; If-Modified-Since</code>类似，只不过将资源修改时间改成了资源内容的 hash，该 hash 在资源响应头的 ETag 中体现，在之后的资源请求头的 If-None-Match 中传回，服务器进行对比，如果一致则返回 304，否则正常返回资源。<!-- raw HTML omitted --><strong>ETag 的优先级要高于 Last-Modified</strong></p>
<h3 id="memory-cache-vs-disk-cache">Memory Cache vs Disk Cache</h3>
<p>Chrome DevTools 有时会观察到<code>(from memory cache)</code>，有时则是<code>(from disk cache)</code>，可以通过以下操作复现</p>
<ol>
<li>资源设置为<code>Cache-Control: max-age=86400</code>，首次打开页面时，响应 200，返回资源</li>
<li>刷新页面，显示 from memory cache</li>
<li>关闭页面，重新打开，显示 from disk cache</li>
</ol>
<p>此外，还可以观察到一个现象，from memory cache 时，耗时很短，往往是 0ms；而 from disk cache 时，往往需要消耗几毫秒的时间。说明确实是前者从内存里读，后者从硬盘里读。</p>
<h3 id="no-cache-vs-no-store">no-cache vs no-store</h3>
<p>用下面的场景进行说明。假设在 HTML 中，同步地请求同一个资源两次（按顺序写两遍），之后异步地再请求一次。</p>
<ol>
<li>当资源被设置为 no-cache 时，该资源只被请求了一次，说明 no-cache 的语义实际上是下次打开页面时，要重新请求资源，但在当前页面上的请求，有缓存的时候还是可以放心用的</li>
<li>资源被设置为 no-store 时，该资源被请求三次，说明 no-store 无论无何都会发送请求（猜测，和名字体现出来的一样，no-store 根本就没把资源存储下来）</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>宽松相等与逻辑比较</title>
			<link>https://yunbo.li/posts/loose-equality-and-comparison/</link>
			<pubDate>Tue, 03 Oct 2017 21:19:56 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/loose-equality-and-comparison/</guid>
			<description>&lt;p&gt;首先来灵魂拷问一下，你知道下面这些表达式的结果吗？&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的所有表达式结果都为 true，怎么样，你答对了吗？&lt;/p&gt;
&lt;p&gt;如果你能准确又自信的说出所有结果的话，说明你已经对 JS 中的宽松相等和类型转换相当了解啦，这篇文章对你可能帮助不大。如果你对结果有一点含糊的话，不妨跟着我一起探索下其中的知识点吧！&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>首先来灵魂拷问一下，你知道下面这些表达式的结果吗？</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;0&#34;</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;</span> <span style="color:#f92672">==</span> [];
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> [];
</span></span><span style="display:flex;"><span><span style="color:#f92672">!</span>[] <span style="color:#f92672">==</span> [];
</span></span></code></pre></div><p>上面的所有表达式结果都为 true，怎么样，你答对了吗？</p>
<p>如果你能准确又自信的说出所有结果的话，说明你已经对 JS 中的宽松相等和类型转换相当了解啦，这篇文章对你可能帮助不大。如果你对结果有一点含糊的话，不妨跟着我一起探索下其中的知识点吧！</p>
<h2 id="抽象操作">抽象操作</h2>
<p>如果读过 ES 规范文档的话，你会发现文档里充斥着各种复杂的运算，为了方便描述这些复杂运算，文档作者往往会给这些运算起个名字，方便在其他地方引用，这些复杂运算就是抽象操作。在真正开始介绍宽松相等的规则之前，让我们先来了解一下 ES 规范中几种和类型转换相关的抽象操作吧。</p>
<h3 id="tostring">ToString</h3>
<p>ToString 操作会将其他类型的值转换成字符串类型，该操作具有以下规则</p>
<ol>
<li>null -&gt; &ldquo;null&rdquo;</li>
<li>undefined -&gt; &ldquo;undefined&rdquo;</li>
<li>true -&gt; &ldquo;true&rdquo;</li>
<li>false -&gt; &ldquo;false&rdquo;</li>
<li>数字的转换遵循通用规则，但极大或极小的值会采用指数形式</li>
<li>对于其他对象，若没有定义<code>toString</code>方法，则输出内部属性<code>[[Class]]</code></li>
</ol>
<p>其他几条规则都比较明确，让我们来重点关注下第 6 条。JS 中除了普通对象之外，大多数都定义了自己的<code>toString</code>方法，比如<code>Function.prototype.toString</code>会输出函数源码，<code>Array.prototype.toString</code>会把值转换成字符串后在每个值中间插入逗号输出。这时候要获取<code>[[Class]]</code>属性的操作就要借助<code>Object.prototype.toString</code>来实现了，也就是我们非常熟悉的<code>Object.prototype.toString.call(obj)</code>操作了。</p>
<h3 id="tonumber">ToNumber</h3>
<p>ToNumber 操作会将其他类型的值转换成数字类型，该操作具有以下规则</p>
<ol>
<li>true -&gt; 1</li>
<li>false -&gt; 0</li>
<li>undefined -&gt; NaN</li>
<li>null -&gt; 0</li>
<li>字符串的转换遵循通用规则，但以 0 开头的八进制数无法正确识别。注意，空字符串转换后为 0</li>
<li>对于其他对象，会对其进行 ToPrimitive 抽象操作，期待得到基本类型的值，然后对该基本类型的值进行 ToNumber 操作</li>
</ol>
<p>这里有一个特殊的地方要注意，<code>undefined</code>会被转换成<code>NaN</code>，而<code>null</code>则会被转换成<code>0</code>。另外<code>ToPrimitive</code>具体对应什么样的操作呢？</p>
<h3 id="toprimitive">ToPrimitive</h3>
<p>ToPrimitive 操作会将其他类型的值转换成基础类型的值，该操作具有以下规则</p>
<ol>
<li>调用对象的 valueOf()方法，如果返回基本类型值，则返回该值，否则继续</li>
<li>调用对象的 toString()方法，如果返回基本类型值，则返回该值，否则抛出 TypeError 异常</li>
</ol>
<p>除非特意实现过，否则大部分对象的 valueOf 方法会返回自身，所以实际环境中，ToPrimitive 操作大部分时候是依赖 toString 方法完成的。注意，刚刚说的只是实践经验，具体操作时必须严格按照规范来。</p>
<h3 id="toboolean">ToBoolean</h3>
<p>ToBoolean 操作会将其他类型的值转换成布尔类型值。首先 ES 规范中先规定了一个假值列表</p>
<ol>
<li>undefined</li>
<li>null</li>
<li>false</li>
<li>±0/NaN</li>
<li><code>&quot;&quot;</code></li>
</ol>
<p>于是 ToBoolean 操作的规则是这样的</p>
<ol>
<li>假值列表中的值 -&gt; false</li>
<li>其他值 -&gt; true</li>
</ol>
<p>很简单对吧，不在假值列表中的值就是 true。</p>
<h2 id="宽松相等">宽松相等</h2>
<p>说起宽松相等，大部分开发者对它深恶痛绝，因为有太多反直觉的结果，所以大部分代码规范都要求开发者只使用严格相等，不要使用宽松相等。甚至有开发者整理了 JS 中各种典型值的相等关系表，可谓非常壮观。</p>
<p><img src="https://i.loli.net/2019/10/31/IbaNf4o8gTP3MVr.png" alt="Equality in JavaScript"></p>
<p>但我觉得逃避解决不了问题，知其所以然才能有效避坑，所以让我们来探究一下宽松相等的原理。</p>
<p>首先，宽松相等和严格相等的区别是，宽松相等允许对操作符两侧的值按照“一定规则”进行类型转换。注意这里的“类型转换”，看起来它就是“万恶之源”了。让我们一起看看刚刚提到的“一定规则”具体是什么样的</p>
<ol>
<li>运算符两侧分别是字符串和数字时，对字符串进行 ToNumber 操作</li>
<li>运算符两侧分别是布尔值和其他类型值时，对布尔值进行 ToNumber 操作</li>
<li>运算符两侧分别是 null 和 undefined 时，返回 true</li>
<li>运算符两侧分别是对象和数字/字符串时，对对象进行 ToPrimitive 操作</li>
</ol>
<p>了解完规则之后，我们来拿文章最开始的例子检验一下。就拿<code>![] == []</code>来说吧，它的结果为 true，很反直觉对吧，空数组居然和它的取反相等，让我们来看一下 true 是被怎么推导出来的。</p>
<ol>
<li>对符号左侧表达式<code>![]</code>求值，由于<code>[]</code>不在假值列表里，所以<code>![]</code>的结果为 false</li>
<li><code>false == []</code>按照规则，需要对 false 进行 ToNumber 操作，结果为 0</li>
<li><code>0 == []</code>一侧是对象，一侧是数字，所以对对象进行 ToPrimitive 操作</li>
<li><code>[].valueOf()</code>返回结果是<code>[]</code>，不符合要求</li>
<li><code>[].toString()</code>返回结果是<code>&quot;&quot;</code>，符合要求</li>
<li><code>0 == &quot;&quot;</code>按照规则，对<code>&quot;&quot;</code>进行 ToNumber 操作，结果是 0</li>
<li><code>0 == 0</code>结果为 true</li>
</ol>
<p>怎么样，对照规则一步步走下来，逻辑是不是清晰多了？</p>
<h2 id="逻辑比较">逻辑比较</h2>
<p>所谓逻辑比较就是大于小于这些，逻辑比较中也是存在类型转换的。其转换规则如下</p>
<ol>
<li>如果双方不都是字符串
<ol>
<li>对双方进行 ToPrimitive 操作</li>
<li>如果结果依然存在非字符串， 则对结果再次进行 ToNumber 操作，比较数字大小</li>
<li>如果结果均为字符串，则按字符串情况比较</li>
</ol>
</li>
<li>双方都是字符串，则按字母顺序比较</li>
</ol>
<p>值得注意的一点是，大于等于和小于等于是通过逻辑取反得到结果的，也就说<code>(a &gt;= b) === true</code>成立的条件是，<code>(a &lt; b) === false</code>成立。这可能会造成一些反直觉的结果。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">a</span> <span style="color:#f92672">=</span> { <span style="color:#a6e22e">n</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">42</span> };
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">b</span> <span style="color:#f92672">=</span> { <span style="color:#a6e22e">n</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">43</span> };
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">a</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">b</span>; <span style="color:#75715e">// -&gt; false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">a</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">b</span>; <span style="color:#75715e">// -&gt; false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">a</span> <span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">b</span>; <span style="color:#75715e">// -&gt; false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">a</span> <span style="color:#f92672">&lt;=</span> <span style="color:#a6e22e">b</span>; <span style="color:#75715e">// -&gt; true
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">a</span> <span style="color:#f92672">&gt;=</span> <span style="color:#a6e22e">b</span>; <span style="color:#75715e">// -&gt; true
</span></span></span></code></pre></div><p>结果乍一看非常反直觉，前三个表达式说明 a 既不大于 b，也不小于 b，也不等于 b，但是后面的大于等于和小于等于却是 true。其实结合上面说的很容易理解，比如<code>a &lt;= b</code>的结果是由<code>a &gt; b</code>的结果推导出来的，因此才是 true。</p>
<p>总结一下，虽然 JS 中的宽松相等和逻辑比较有很多反直觉的结果，但其背后都有规则可循，掌握了这些规则之后，我们发现宽松相等也不是那么坑，比如<code>obj == null</code>就能一下子判断出 obj 是不是 null 或者 undefined 这两种情况，很实用，也很安全。希望大家看了这篇文章之后能摘掉有色眼镜来看待<code>==</code>运算符，并对它加以合理利用。</p>
]]></content>
		</item>
		
		<item>
			<title>学习使用正则表达式</title>
			<link>https://yunbo.li/posts/regexp-in-js/</link>
			<pubDate>Mon, 18 Jul 2016 20:40:33 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/regexp-in-js/</guid>
			<description>&lt;p&gt;正则表达式很早就有了解，有需要的时候，也能借助搜索引擎简单使用一下，但似乎从来没有比较系统的学习过，所以这段时间看了一些资料，记录一下正则表达式的一些概念、用法以及 JS 中的相关 API。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>正则表达式很早就有了解，有需要的时候，也能借助搜索引擎简单使用一下，但似乎从来没有比较系统的学习过，所以这段时间看了一些资料，记录一下正则表达式的一些概念、用法以及 JS 中的相关 API。</p>
<h3 id="字符组">字符组</h3>
<p>字符组包含了要匹配字符的所有可能情况，使用<code>[]</code>表示，具体有如下三种描述方法</p>
<ol>
<li>列举。即列举所有可能字符，如<code>[abc]</code></li>
<li>范围。即使用范围表示所有可能字符，如 <code>[a-z]</code></li>
<li>取反。在上面两种写法的基础上，在前面加上<code>^</code>表示取反逻辑，如<code>[^abc]</code></li>
</ol>
<p>对于常见的字符组，正则引擎内置了一些简写</p>
<ol>
<li><code>\d</code>表示<code>[0-9]</code></li>
<li><code>\D</code>表示<code>[^0-9]</code></li>
<li><code>\w</code>表示<code>[0-9a-zA-Z_]</code></li>
<li><code>\W</code>表示<code>[^0-9a-zA-Z_]</code></li>
<li><code>\s</code>表示<code>[ \t\v\n\r\f]</code>，\v 是垂直制表符，\f 是换页符</li>
<li><code>\S</code>表示<code>[^ \t\v\n\r\f]</code></li>
<li><code>.</code>表示<code>[^\n\r\u2028\u2029]</code>表示几乎所有字符，除了换行符、回车符、行分隔符、段分隔符</li>
</ol>
<h3 id="量词">量词</h3>
<p>量词用来描述某个模式出现的次数，有两种描述方法</p>
<ol>
<li><code>{m,}</code>表示至少出现 m 次</li>
<li><code>{m}</code>表示恰好出现 m 次</li>
</ol>
<p>同样，对于常用的量词，引擎内置了一些简写</p>
<ol>
<li><code>?</code>表示<code>{0, 1}</code></li>
<li><code>+</code>表示<code>{1,}</code></li>
<li><code>*</code>表示<code>{0,}</code></li>
</ol>
<h3 id="贪婪与非贪婪">贪婪与非贪婪</h3>
<p>先看一个例子</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">regex</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\d{1,3}/g</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;1 12 123 1234&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">string</span>.<span style="color:#a6e22e">match</span>(<span style="color:#a6e22e">regex</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">// -&gt; [&#34;1&#34;, &#34;12&#34;, &#34;123&#34;, &#34;123&#34;, &#34;4&#34;]
</span></span></span></code></pre></div><p><code>\d{1,3}</code>的意思是数字出现 1 到 3 次，但从结果可以观察到，这个模式会尽量多的匹配数字，即能匹配 3 个就不匹配两个，能匹配两个就不匹配一个。这种行为就是贪婪匹配。那么什么是非贪婪匹配的，在原来的例子上简单修改一下。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">regex</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\d{1,3}?/g</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;1 12 123 1234&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">string</span>.<span style="color:#a6e22e">match</span>(<span style="color:#a6e22e">regex</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">// -&gt; [&#34;1&#34;, &#34;1&#34;, &#34;2&#34;, &#34;1&#34;, &#34;2&#34;, &#34;3&#34;, &#34;1&#34;, &#34;2&#34;, &#34;3&#34;, &#34;4&#34;]
</span></span></span></code></pre></div><p>匹配出来的结果都是一个数字，即能匹配一个就不匹配两个。通过上面的例子可以得到两个结论</p>
<ol>
<li>量词默认是贪婪的</li>
<li>在模式后面加<code>?</code>可以将贪婪匹配变成非贪婪匹配</li>
</ol>
<h3 id="选择分支">选择分支</h3>
<p>语法是<code>(p1|p2)</code>，表示模式 p1 或模式 p2。看个例子</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">regex</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\d{3}|\d{5}/g</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;12345&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">string</span>.<span style="color:#a6e22e">match</span>(<span style="color:#a6e22e">regex</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">// -&gt; [&#34;123&#34;]
</span></span></span></code></pre></div><p>从这个例子也可以看出来，选择分支是非贪婪的，即 p1 满足了，就不再去尝试匹配 p2 了。</p>
<h3 id="匹配位置">匹配位置</h3>
<p>除了匹配字符，正则表达式的一个重磅功能就是匹配位置，即开头、结尾和字符之间的位置。一些常用的表示方法如下</p>
<ol>
<li><code>^</code> 匹配开头，多行模式下匹配行开头</li>
<li><code>$</code> 匹配结尾，多行模式下匹配行结尾</li>
<li><code>\b</code> 匹配\w 和\W 的位置，自然就有包括\w 和^以及\w 和$之间的位置</li>
<li><code>\B</code> 是<code>\b</code>取反</li>
<li><code>(?=p)</code> 模式 p 前面的位置 positive lookahead</li>
<li><code>(?!p)</code> 上面的逻辑取反 negative lookahead</li>
</ol>
<p><strong>2019-5-30 更新，ES6 中新增了两个位置</strong></p>
<ol>
<li>(?&lt;=p) 模式 p 后面的位置 positive lookbehind</li>
<li>(?&lt;!p) 上面的逻辑取反 negative lookbehind</li>
</ol>
<h3 id="引用分组">引用分组</h3>
<p>被<code>()</code>包裹起来的模式可以在正则表达式中和 JS 相关 API 中被引用，当被正则表达式引用时，使用<code>\数字</code>表示，在被 JS 相关 API 引用时，使用<code>$数字</code>表示，其中的<code>数字</code>范围从 1 到 99。</p>
<p>下面通过几个例子展示一下具体的用法</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">regex</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\d{4}(-|\/)\d{2}\1\d{2}/</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string1</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;2016-07-18&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string2</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;2016/07/18&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string3</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;2016-07/18&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">regex</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">string1</span>); <span style="color:#75715e">// -&gt; true
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">regex</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">string2</span>); <span style="color:#75715e">// -&gt; true
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">regex</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">string3</span>); <span style="color:#75715e">// -&gt; false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">regex</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/(\d{4})-(\d{2})-(\d{2})/</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">string</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;2016-07-18&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">string</span>.<span style="color:#a6e22e">replace</span>(<span style="color:#a6e22e">regex</span>, <span style="color:#e6db74">&#34;$2/$3/$1&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">result</span>); <span style="color:#75715e">// -&gt; 07/18/2016
</span></span></span></code></pre></div><h3 id="元字符">元字符</h3>
<p>在编写正则表达式时，遇到下面这 20 个字符，要记得转义</p>
<ol>
<li><code>\ | /</code></li>
<li><code>() [] {}</code></li>
<li><code>+ - * =</code></li>
<li><code>. , : ! ?</code></li>
<li><code>^ $</code></li>
</ol>
<h3 id="js-api">JS API</h3>
<p>在 JS 中可以通过如下 API 使用正则表达式完成查找、替换、验证等操作</p>
<ol>
<li>regex.test(string)</li>
<li>regex.exec(string)</li>
<li>string.search(regex)</li>
<li>string.match(regex)</li>
<li>string.split(regex)</li>
<li>string.replace(regex, string/function)</li>
</ol>
<h3 id="其他有趣的点">其他有趣的点</h3>
<p>如果要匹配任意字符，可以使用逻辑取反操作来完成，比如<code>[\d\D]</code> / <code>[\s\S]</code> / <code>[\w\W]</code> / <code>[^]</code></p>
<p>如果要写一个什么都匹配不了的模式，可以这样：<code>/.^/</code>即任意字符后面有个开头，这当然是不存在的</p>
]]></content>
		</item>
		
		<item>
			<title>Javascript中实现继承的几种方式</title>
			<link>https://yunbo.li/posts/inheritance-in-js/</link>
			<pubDate>Tue, 15 Mar 2016 10:18:40 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/inheritance-in-js/</guid>
			<description>&lt;p&gt;在ES6到来之前，JS中一直没有类的概念。而类本身又是一种非常符合人类思考方式的抽象模型，所以虽然JS不支持类，但依然可以使用功能强大的原型机制来模拟类的各种行为。经过多年实践总结，开发者们逐渐形成了一些利用原型链实现类和继承的方式，通过比较这些实现方式的优缺点，我们也能更好地理解原型链机制。正是因为这些实践经验，才孕育出了今天给我们带来无限便利的ES6中类的机制。接下来让我们分析一下各种实现方式的特点吧！&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>在ES6到来之前，JS中一直没有类的概念。而类本身又是一种非常符合人类思考方式的抽象模型，所以虽然JS不支持类，但依然可以使用功能强大的原型机制来模拟类的各种行为。经过多年实践总结，开发者们逐渐形成了一些利用原型链实现类和继承的方式，通过比较这些实现方式的优缺点，我们也能更好地理解原型链机制。正是因为这些实践经验，才孕育出了今天给我们带来无限便利的ES6中类的机制。接下来让我们分析一下各种实现方式的特点吧！</p>
<h3 id="朴素的原型链继承">朴素的原型链继承</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SuperType</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Han&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">friends</span> <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;A&#34;</span>, <span style="color:#e6db74">&#34;B&#34;</span>];
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SuperType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">getName</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SubType</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">18</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SuperType</span>();
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">constructor</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">SubType</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">getAge</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span>;
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>让我们声明两个<code>SubType</code>的实例试一下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p1</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SubType</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p2</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SubType</span>();
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">getName</span>(); <span style="color:#75715e">// -&gt; &#39;Han&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">getName</span>(); <span style="color:#75715e">// -&gt; &#39;Han&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">getAge</span>(); <span style="color:#75715e">// -&gt; 18
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Xiaohan&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">getName</span>(); <span style="color:#75715e">// -&gt; &#39;Xiaohan&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">p2</span>.<span style="color:#a6e22e">getName</span>(); <span style="color:#75715e">// -&gt; &#39;Han&#39;
</span></span></span></code></pre></div><p>看起来是符合预期的对吧，子类继承了父类上定义的属性和方法，并且修改子类实例的属性时，不同实例间不会发生干扰。真的是这样吗？</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">friends</span>.<span style="color:#a6e22e">push</span>(<span style="color:#e6db74">&#34;C&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p2</span>.<span style="color:#a6e22e">friends</span>; <span style="color:#75715e">// -&gt; [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;]
</span></span></span></code></pre></div><p>等等，改了<code>p1.friends</code>之后，为什么另一个实例<code>p2</code>也被影响了？刚刚改<code>p1.name</code>的时候明明是正常的！让我们冷静一下，回头看看最开始继承的实现。由于<code>SubType.prototype = new SuperType()</code>，父类的属性实际上是声明在子类的原型(即<code>__proto__</code>，以下统一称为原型)上的，因此<code>p1.name</code>实际上是<code>p1.__proto__.name</code>，所以<strong>父类属性实际上被所有子类实例共享</strong>。我们知道操作对象属性时，内部操作<code>[[Get]]</code>和<code>[[Set]]</code>的逻辑时不一样的，<code>[[Get]]</code>会沿着原型链向上查找，而<code>[[Set]]</code>则不会，这就会造成<strong>属性遮蔽</strong>现象。因此当我们执行<code>p1.name = 'Xiaohan'</code>时，触发<code>[[Set]]</code>操作，此时<code>name</code>属性就被定义在<code>p1</code>上了，之后在取该属性的值时，由于<code>p1</code>上已经有该属性了，因此不会继续沿原型链向上查找，所以赋值行为在不同子类实例间看起来是正常的。如果不是赋值操作，那可就要小心了，比如<code>p1.friends.push('C')</code>，这并不是一个赋值操作，而且直接操作了<code>p1.friends</code>属性，按照我们刚刚说的，本质上操作的是<code>p1.__proto__.friends</code>，而这个属性是被所有实例共享的！所以很自然的，<code>p2.friends</code>也被影响了。</p>
<p>所以，总结下原型链继承的问题：</p>
<ol>
<li>父类属性会被所有子类实例共享，如果操作不慎(比如上面例子中的对数组进行<code>push</code>操作等)，会影响所有实例，造成诡异的问题</li>
<li>父类属性在声明时就被写死了，不能由子类定义，使用上不够灵活</li>
</ol>
<h3 id="经典继承借用构造函数">经典继承(借用构造函数)</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SuperType</span>(<span style="color:#a6e22e">name</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">friends</span> <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;A&#34;</span>, <span style="color:#e6db74">&#34;B&#34;</span>];
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">getName</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SubType</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">SuperType</span>.<span style="color:#a6e22e">call</span>(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#34;Han&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">18</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">getAge</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>在经典继承中，子类借用了父类的构造函数，将父类声明的属性和方法通过“借用”的方式声明在自己身上，从而既达到了继承的目的，又避免了原型链继承带来的子类实例共享父类属性的问题。但通过观察该实现，我们不难发现父类的所属性和方法在每个子类实例上都有一份儿，虽然实例间不会相互影响了，但本该得到复用的父类和子类方法，并没有真正被复用，造成了极大的资源浪费。</p>
<h3 id="组合继承伪经典继承">组合继承(伪经典继承)</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SuperType</span>(<span style="color:#a6e22e">name</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">friends</span> <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;A&#34;</span>, <span style="color:#e6db74">&#34;B&#34;</span>];
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SuperType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">getName</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SubType</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">SuperType</span>.<span style="color:#a6e22e">call</span>(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#34;Han&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">18</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SuperType</span>();
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">constructor</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">SubType</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">getAge</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span>;
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>顾名思义，组合继承结合了原型链继承和经典继承的做法，既能复用父类和子类方法，又能通过借用构造函数的方式，将父类属性定义在每个子类实例上，达到不相互干扰的目的。那么组合继承可以称得上完美的继承实现了吗？</p>
<p>并不是，让我们对照实现代码仔细分析一下。子类内部<code>SuperType.call(this, &quot;Han&quot;)</code>和<code>SubType.prototype = new SuperType()</code>造成的结果是父类构造函数被调用了两次，该操作造成的结果是父类属性同时存在于子类实例和子类实例的原型上！因为上面提到的属性遮蔽现象，所以这种冗余对于开发者来说是感知不到的，但冗余毕竟是事实存在的，虽然不影响结果，但依然是一种资源浪费。</p>
<h3 id="原型式继承">原型式继承</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">person</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Han&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">getName</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p1</span> <span style="color:#f92672">=</span> Object.<span style="color:#a6e22e">create</span>(<span style="color:#a6e22e">person</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Xiaohan&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">p1</span>.<span style="color:#a6e22e">getName</span>(); <span style="color:#75715e">// -&gt; Xiaohan
</span></span></span></code></pre></div><p>原型式继承由知名大神Douglas Crockford提出，借助<code>Object.create</code>一脚踢开Function，直接借用原型链实现对象与对象之间的继承关系。这种继承的实现方法，思路简单直接，不会带来<code>new来new去</code>引起的各种“坑”，但由于出现比较晚，所以用的人相对来说也没那么多。YDKJS的作者Kyle Simpson倒是对这种模式赞赏有加，在其书中用了很大篇幅介绍它的优点和灵活性。</p>
<h3 id="寄生式继承">寄生式继承</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">createAnotherPerson</span>(<span style="color:#a6e22e">original</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">clone</span> <span style="color:#f92672">=</span> Object.<span style="color:#a6e22e">create</span>(<span style="color:#a6e22e">original</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">clone</span>.<span style="color:#a6e22e">sayName</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;name is&#34;</span>, <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span>);
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">clone</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">person</span> <span style="color:#f92672">=</span> { <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Han&#34;</span> };
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">han</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createAnotherPerson</span>(<span style="color:#a6e22e">person</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">han</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;xiaohan&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">han</span>.<span style="color:#a6e22e">sayName</span>();
</span></span></code></pre></div><p>寄生式继承可以理解为位于原型式继承的扩展，它使用了工厂函数来创建实例，并为每个实例添加方法，增加了方法的可复用性。</p>
<h3 id="组合寄生式继承">组合寄生式继承</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">inheritPrototype</span>(<span style="color:#a6e22e">subType</span>, <span style="color:#a6e22e">superType</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">subType</span>.<span style="color:#a6e22e">prototype</span> <span style="color:#f92672">=</span> Object.<span style="color:#a6e22e">create</span>(<span style="color:#a6e22e">superType</span>.<span style="color:#a6e22e">prototype</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">subType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">constructor</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">subType</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SuperType</span>(<span style="color:#a6e22e">name</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">friends</span> <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;A&#34;</span>, <span style="color:#e6db74">&#34;B&#34;</span>];
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SuperType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">getName</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">SubType</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">SuperType</span>.<span style="color:#a6e22e">call</span>(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#34;Han&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">18</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">inheritPrototype</span>(<span style="color:#a6e22e">SubType</span>, <span style="color:#a6e22e">SuperType</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">constructor</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">SubType</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">SubType</span>.<span style="color:#a6e22e">prototype</span>.<span style="color:#a6e22e">getAge</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span>;
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>在初看《红宝书》时，对继承这一块儿的内容安排很困惑，前面都是在用函数实现继承，怎么突然插入了原型式继承和寄生式继承呢？看到组合寄生式继承时，我释然了。在<code>Object.create</code>出现之前，开发者们一直使用<code>new</code>来构建原型链，这多多少少有些晦涩难懂，而<code>Object.create</code>简单直接的逻辑，无疑大大简化了构建原型链的难度，那能不能用<code>Object.create</code>来解决一下上面提到的几种继承实现的问题呢？</p>
<p>答案是肯定的，让我们的目光回到组合继承。我们提到组合继承的缺点是父类构造函数被调用两次，造成了势必被遮蔽的父类属性出现在了子类实例的原型链上，这是一种资源浪费。那有没有办法避免这种资源浪费呢？我们既要构造<code>SubType.prototype</code>和<code>SuperType.prototype</code>的原型链关系，又要避免<code>SuperType</code>的冗余调用，防止资源浪费。显然，这时候该<code>Object.create</code>登场了。观察上面的代码，<code>inheritPrototype</code>就达到了我们的目的：既构造了原型链关系，又避免了这个过程中<code>SuperType</code>的调用。至此，继承的实现算是达到了比较完善的程度。</p>
<p>我们知道，ES6的类只是语法糖，其背后依然是基于原型链实现的，其实现方式和组合寄生式继承大致相同，但又有细微差别，未来有时间会总结下ES6的类是如何实现的。</p>
]]></content>
		</item>
		
		<item>
			<title>加权平均成绩计算器</title>
			<link>https://yunbo.li/posts/weighted-average-score-calculator/</link>
			<pubDate>Tue, 14 Jul 2015 22:54:20 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/weighted-average-score-calculator/</guid>
			<description>&lt;p&gt;大三刚刚结束，学院根据前三个学年的成绩发布了预排名表。在签字确认之前当然还是自己算算比较放心，所以写了这个小脚本用来计算自己上学以来的所有学科的加权平均分（不包括任选课）&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>大三刚刚结束，学院根据前三个学年的成绩发布了预排名表。在签字确认之前当然还是自己算算比较放心，所以写了这个小脚本用来计算自己上学以来的所有学科的加权平均分（不包括任选课）</p>
<h3 id="截图">截图</h3>
<p>因为教务系统加了验证码，所以这次使用了 GUI。但是我只会用 Tkinter，所以别期待太好看。大概就长下面这样了。</p>
<p>登录界面：</p>
<p><img src="/img/weighted-average-score-calculator/login.png" alt="登录界面"></p>
<p>错误提示：</p>
<p><img src="/img/weighted-average-score-calculator/error.png" alt="错误提示"></p>
<p>主界面：</p>
<p><img src="/img/weighted-average-score-calculator/main.png" alt="主界面"></p>
<h3 id="使用说明">使用说明</h3>
<h4 id="源码方式">源码方式</h4>
<p>在使用源码方式运行前，请确保你已经安装了 Python 环境（目前只支持 2.X），并安装了<code>requests</code>、<code>beautifulsoup4</code>、<code>Pillow</code>这三个库（项目源码中附带了<code>requirements.txt</code>文件）</p>
<p>从<a href="https://github.com/hansnow/bupt-wavg-score">项目地址</a>克隆源码，然后执行<code>./score.py</code>即可。</p>
<h4 id="可执行文件未完成">可执行文件（未完成）</h4>
<p>双击下载下来的 EXE 文件即可。
计划用 cx_Freeze 打包一下，估计明天才有时间弄了。</p>
<h3 id="写在后面">写在后面</h3>
<ul>
<li>强烈推荐使用源码方式运行，程序代码都看得到，如果出了问题自己调试起来也方便</li>
<li>非常不愿意打包 EXE，毕竟直接给可执行文件的话总会有人说三道四。如果选择可执行文件方式运行的话请自行承担所有风险</li>
<li>草草发布，我只在 Win8 下测试了。肉身不在学校，只能 Mac 开虚拟机开发，超苦逼的。不过我猜其他环境问题应该不大，尤其是*nix 系，最可能出问题的地方应该是字符串编码</li>
<li>代码在<a href="https://github.com/hansnow/bupt-wavg-score">GitHub</a>上，使用中出现什么问题可以直接在这篇文章下评论或者在去<a href="https://github.com/hansnow/bupt-wavg-score">GitHub</a>开 issue</li>
<li>祝大家保研/出国/考研成功！</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>给妹子看的简易翻墙教程</title>
			<link>https://yunbo.li/posts/gfw-bypass-tutorial-for-someone/</link>
			<pubDate>Sat, 11 Jul 2015 15:53:01 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/gfw-bypass-tutorial-for-someone/</guid>
			<description>&lt;p&gt;先简单介绍一下翻墙的原理。由于墙的存在，我们的电脑不能和某些网站建立连接，这些网站我们叫做“被 GFW 认证的网站”。于是，我们需要一台既可以和我们连接，又可以和“被 GFW 认证的网站”连接的服务器进行中转。所以所谓的翻墙就是通过中转服务器来访问网站。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>先简单介绍一下翻墙的原理。由于墙的存在，我们的电脑不能和某些网站建立连接，这些网站我们叫做“被 GFW 认证的网站”。于是，我们需要一台既可以和我们连接，又可以和“被 GFW 认证的网站”连接的服务器进行中转。所以所谓的翻墙就是通过中转服务器来访问网站。</p>
<h3 id="下载">下载</h3>
<p>我们需要用到的软件是 shadowsocks，你可以从<a href="https://github.com/shadowsocks/shadowsocks-csharp/releases">这里下载</a></p>
<p>需要说明的一点是：对于<strong>Win8 及之后</strong>的系统，需要选择<code>Shadowsocks-win-dotnet4.0-x.x.x.zip</code>这种安装包，对于<strong>Win7 及之前的系统</strong>，需要选择<code>Shadowsocks-win-x.x.x.zip</code>这种安装包。</p>
<p>ps. 因为下载服务器位于 Github，所有有些时候不翻墙是无法下载的，于是这个问题就变成了先有鸡还是先有蛋的问题。对于这种情况，我强烈建议找你男朋友解决。</p>
<h3 id="配置">配置</h3>
<p>下载完软件之后，我们得到了一个可爱的压缩包，果断解压。然后我们就得到了一个名为<code>Shadowsocks.exe</code>的文件，找一个你喜欢的地方放置它，比如我们把它放在 D 盘的 Shadowsocks 目录下。</p>
<p>放置好之后，双击打开。跳出了一个配置页面，在这个配置页面中填入我给你的服务器信息（服务器 IP、服务器端口、密码、加密方式），其他选项保持默认就好，填好以后点击确定。</p>
<p><img src="/img/gfw-bypass-tutorial-for-someone/config.png" alt="配置"></p>
<p>这时候它就非常调皮的跑到右下角的系统托盘里了。</p>
<p><img src="/img/gfw-bypass-tutorial-for-someone/tray.png" alt="托盘气泡"></p>
<p>如果你没找到他，那很可能是隐藏在托盘最左侧的小三角里面了。</p>
<p><img src="/img/gfw-bypass-tutorial-for-someone/tray-folded.png" alt="托盘里的小三角"></p>
<p>他老在小三角里躲着也不是个事儿，所以我们来配置一下，让它始终显示在系统托盘里。点击系统托盘最左边的小三角，然后点击<code>自定义</code>，找到 shadowsocks，并将右侧的行为改成“显示图标和通知”，然后就大功告成了。</p>
<p><img src="/img/gfw-bypass-tutorial-for-someone/tray-config.png" alt="托盘设置"></p>
<h3 id="使用">使用</h3>
<h4 id="easy-模式">Easy 模式</h4>
<p>在系统托盘的 shadowsocks 图标上右键，勾选“启用系统代理”，在“系统代理模式”中选择“PAC 模式”，勾选“开机启动”。</p>
<p><img src="/img/gfw-bypass-tutorial-for-someone/pac.png" alt="PAC"></p>
<p>做完上面这几步之后就可以愉快的上网了，Facebook、Google、Twitter、YouTube 全都无障碍访问。</p>
<p>但是，Easy 模式有一个缺陷：只能绕过一些常见的“被 GFW 认证的网站”，对于一些比较小众，但依然被“GFW 认证”的网站就无能为力了。为了解决这种问题，一个简单粗暴的解决方式是在“系统代理模式”中选择“全局模式”，这时候所有的访问会全部由代理中转，也就解决了那些小众网站不能访问的问题。但是这又带来了一个新的问题，既然是所有流量都会经过中转服务器，那么访问境内网站的时候就会非常慢，于是你又得切回“PAC 模式”，这么切来切去的不是个办法，所以想完美解决这个问题我们就要学习下面的 Hard 模式。</p>
<h4 id="hard-模式">Hard 模式</h4>
<p>Hard 模式说起来比较复杂，以后慢慢补充。</p>
]]></content>
		</item>
		
		<item>
			<title>病中杂记</title>
			<link>https://yunbo.li/posts/catch-cold-and-headache-in-2015/</link>
			<pubDate>Sun, 14 Jun 2015 22:15:53 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/catch-cold-and-headache-in-2015/</guid>
			<description>&lt;blockquote&gt;
&lt;p&gt;孤独的人是可耻的 ——张楚&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;生病的人是痛苦的 ——我&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;不知道隔了多久，又得了我最擅长的感冒。这次感冒来势汹汹，让我很快进入了生病状态。当然我也没怂，当天我就从自习室撤出来了，从此一周再也没进去过。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<blockquote>
<p>孤独的人是可耻的 ——张楚</p></blockquote>
<blockquote>
<p>生病的人是痛苦的 ——我</p></blockquote>
<p>不知道隔了多久，又得了我最擅长的感冒。这次感冒来势汹汹，让我很快进入了生病状态。当然我也没怂，当天我就从自习室撤出来了，从此一周再也没进去过。</p>
<p>说起我这体弱多病的身体，那真是有年头儿了。记得小时候经常发烧，经常到什么程度呢？家里一直备着输液的所有药品和器具，我妈一旦发现我烧起来了，立马输液伺候。而我经常非常争气地在晚上发烧，所以关于小时候发烧的记忆一直是半夜突然被老妈叫醒，迷迷糊糊的爬起来自己握着手腕（哪儿听说过亲妈给亲儿子输液还用压脉带的？），然后感觉手背上被什么东西咬了一下，管不了那么多了，太困。于是接着睡。就这样，晚上发烧，天亮之前解决，第二天“高高兴兴上学去”，丝毫没耽误学习（摔！）</p>
<p>不过长大之后身体好像好了不少，不再病病殃殃的了。高中倒是老感冒，不过发烧的次数应该一只手能数的过来。记得比较清楚的一次是高一刚开学的时候，那时候正在讲化学里的“物质的量”，当时的我觉得这玩意儿真特么难懂啊，什么玩意儿啊，描述一种物质的多少用质量不就得了，干嘛弄出个“摩尔”出来。刚开始的骂娘，后来越来越看不懂了之后竟然像小小姑娘一样觉得委屈——高中真特么难啊。就在这种委屈中，我被病魔战胜了。在这种情况下谁都没办法继续坚持上课对不对，所以我顺利地拿到年级主任签的假条骑着自行车回家了。重点来了，回家的路上我竟然哭了，卧槽，我特么一个大老爷们儿骑着自行车突然哭了！！！现在想想真丢人啊！回家之后还是老一套，被右手握住手腕的左手又被咬了。不过那次生病之后我仿佛基因突变一般突然理解了“物质的量”的概念，这玩意儿不就是数量么，老子终于明白啦咩哈哈哈！紧接着迎来了高中的第一次期中考试，我意外的超长发挥，成功进步一百多名一下子进入了年纪前 20。</p>
<p>这次生病来的太猛烈，以至于我还没准备好就完全失去战斗力了。生病的过程是痛苦的，每天的生活是无聊的。起床——吃饭——上床玩儿手机——吃饭——睡午觉——发呆——吃饭——晚安。前两天买的小鸡手柄可算是用上了，每天无聊的时候就和怪叔叔玩儿双人的 FC 游戏，不亦乐乎。这几天在网上瞎逛的时候看到了邱神很老的博客、chon 的博客和大鹰的博客，有感于大牛的发迹。考研之后的待办清单上又多了几项：ArchLinux、Ruby、i3 等等等还有好多。</p>
<p>今天是生病的第七天，下午终于强迫自己去了久别的自习室。说实话，在考试周即将到来之际，一个星期不去自习室是需要勇气的；一周没去过自习室之后再想去是同样需要勇气的。下午去之前纠结了好久，抱着手机在寝室做了好久，鞋都换好了，但屁股就是不想挪地方。当时我心里有两个小人儿——一个说：别去了吧，歇会儿吧。另一个说：好啊好啊~不过复习不等人啊，我还是抱着电脑去自习室吹空调了。有了电脑之后坐在自习室里就容易多了，心里也舒服很多：老子都已经坐过来了，你还想怎样啊！</p>
<p>鼻子依旧闻不到味道，我想好了，等我问到味道的第一天，我一定去北门买一份加了肉的肠粉好好尝尝，嗯！</p>
<p>以上。</p>
]]></content>
		</item>
		
		<item>
			<title>北邮教务系统评教脚本</title>
			<link>https://yunbo.li/posts/bupt-pingjiao/</link>
			<pubDate>Fri, 09 Jan 2015 09:38:17 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/bupt-pingjiao/</guid>
			<description>&lt;p&gt;&lt;strong&gt;2015 年 01 月 09 日更新&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;由于教务系统的地址变更以及验证码的加入，Python 版的评教脚本已经失效，请使用 javascript 版&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每个学期末都烦心事儿不断，评教这件小事儿还非得过来掺合掺合。扒着眼睛找那个巨小无比的选择按钮真实让人烦躁啊。作为死工科生，总得做点什么。于是就写了个评教脚本。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p><strong>2015 年 01 月 09 日更新</strong></p>
<p><strong>由于教务系统的地址变更以及验证码的加入，Python 版的评教脚本已经失效，请使用 javascript 版</strong></p>
<p>每个学期末都烦心事儿不断，评教这件小事儿还非得过来掺合掺合。扒着眼睛找那个巨小无比的选择按钮真实让人烦躁啊。作为死工科生，总得做点什么。于是就写了个评教脚本。</p>
<p>总共有两个版本（考虑到坑爹的 windows，实际上是三个）</p>
<ul>
<li>JavaScript 版</li>
<li>Python 版</li>
<li>Py4win 版（src+exe）</li>
</ul>
<p>这三个版本有点儿不一样。Javascript 版就是一串 JS 代码，直接在浏览器中运行即可，不需要额外下载安装任何东西，但是缺点是每次只能评一位老师，也就是说你还是得运行十几次代码才能完成教学评估。Python 版是一个.py 文件，在大多数 Linux/OSX 上都能直接运行，而且能一次性完成评教，是真正的“一键评教”。单独把 Windows 割裂出来是因为编码问题，cmd 的默认编码是 gb2312，于是就出现了乱码，于是把 Python 版的代码做一些编码上的处理就变成了 Py4win 版，考虑到大部分 windows 下没安装 Python，于是用 py2exe 打包了一个 exe，算作是 Py4win 版的一部分吧。</p>
<p>下面开始挨个介绍使用方法。</p>
<h2 id="javascript-版一般般推荐">JavaScript 版（一般般推荐）</h2>
<p>复制下面这串代码(有结构的未压缩版请看文章最后)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#a6e22e">javascript</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;URP 综合教务系统 - 教学评估 - 教学评估&#34;</span><span style="color:#f92672">==</span>window.document.<span style="color:#a6e22e">title</span><span style="color:#f92672">?</span><span style="color:#a6e22e">x</span><span style="color:#f92672">=</span>window.<span style="color:#a6e22e">frames</span>[<span style="color:#ae81ff">1</span>].<span style="color:#a6e22e">frames</span>[<span style="color:#ae81ff">2</span>].document.<span style="color:#a6e22e">getElementsByTagName</span>(<span style="color:#e6db74">&#34;input&#34;</span>)<span style="color:#f92672">:</span><span style="color:#e6db74">&#34;问卷评估页面&#34;</span><span style="color:#f92672">==</span>window.document.<span style="color:#a6e22e">title</span><span style="color:#f92672">?</span><span style="color:#a6e22e">x</span><span style="color:#f92672">=</span>window.document.<span style="color:#a6e22e">getElementsByTagName</span>(<span style="color:#e6db74">&#34;input&#34;</span>)<span style="color:#f92672">:</span><span style="color:#a6e22e">alert</span>(<span style="color:#e6db74">&#34;请确认一个你现在是不是处于正确的页面上，本程序只能在教学评估页面运行！&#34;</span>),<span style="color:#a6e22e">data</span><span style="color:#f92672">=</span><span style="color:#66d9ef">new</span> Array;<span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;<span style="color:#a6e22e">i</span><span style="color:#f92672">&lt;</span><span style="color:#a6e22e">x</span>.<span style="color:#a6e22e">length</span>;<span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>)<span style="color:#e6db74">&#34;hidden&#34;</span><span style="color:#f92672">==</span><span style="color:#a6e22e">x</span>[<span style="color:#a6e22e">i</span>].<span style="color:#a6e22e">type</span><span style="color:#f92672">&amp;&amp;</span><span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">x</span>[<span style="color:#a6e22e">i</span>].<span style="color:#a6e22e">value</span>);<span style="color:#66d9ef">function</span> <span style="color:#a6e22e">post</span>(<span style="color:#a6e22e">t</span>,<span style="color:#a6e22e">e</span>,<span style="color:#a6e22e">n</span>){<span style="color:#a6e22e">n</span><span style="color:#f92672">=</span><span style="color:#a6e22e">n</span><span style="color:#f92672">||</span><span style="color:#e6db74">&#34;post&#34;</span>;<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">a</span><span style="color:#f92672">=</span>document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">&#34;form&#34;</span>);<span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#66d9ef">in</span> <span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;method&#34;</span>,<span style="color:#a6e22e">n</span>),<span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;action&#34;</span>,<span style="color:#a6e22e">t</span>),<span style="color:#a6e22e">e</span>)<span style="color:#66d9ef">if</span>(<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">hasOwnProperty</span>(<span style="color:#a6e22e">i</span>)){<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">d</span><span style="color:#f92672">=</span>document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">&#34;input&#34;</span>);<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;type&#34;</span>,<span style="color:#e6db74">&#34;hidden&#34;</span>),<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;name&#34;</span>,<span style="color:#a6e22e">i</span>),<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;value&#34;</span>,<span style="color:#a6e22e">e</span>[<span style="color:#a6e22e">i</span>]),<span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">appendChild</span>(<span style="color:#a6e22e">d</span>)}document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">appendChild</span>(<span style="color:#a6e22e">a</span>),<span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">submit</span>()}<span style="color:#a6e22e">post</span>(<span style="color:#e6db74">&#34;http://jwxt.bupt.edu.cn/jxpgXsAction.do?oper=wjpg&#34;</span>,{<span style="color:#a6e22e">wjbm</span><span style="color:#f92672">:</span><span style="color:#a6e22e">data</span>[<span style="color:#ae81ff">0</span>],<span style="color:#a6e22e">bpr</span><span style="color:#f92672">:</span><span style="color:#a6e22e">data</span>[<span style="color:#ae81ff">1</span>],<span style="color:#a6e22e">pgnr</span><span style="color:#f92672">:</span><span style="color:#a6e22e">data</span>[<span style="color:#ae81ff">2</span>],<span style="color:#e6db74">&#34;0000000021&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;10_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000022&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;10_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000023&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;5_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000024&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;20_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000025&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;10_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000026&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;5_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000027&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;5_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000028&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;20_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000029&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;10_0.95&#34;</span>,<span style="color:#e6db74">&#34;0000000030&#34;</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;5_0.95&#34;</span>,<span style="color:#a6e22e">zgpj</span><span style="color:#f92672">:</span><span style="color:#e6db74">&#34;老师讲课很认真的啦~&#34;</span>},<span style="color:#e6db74">&#34;post&#34;</span>);
</span></span></code></pre></div><p>登录教务系统，打开教学评估页面，选择一门课程进行评估，于是就到了下面这个页面（由于测试的关系我已经评完了）</p>
<p><img src="/img/bupt-pingjiao/questionnaire.png" alt="评教页面"></p>
<p>然后，在浏览器的<strong>地址栏</strong>中粘贴刚刚复制的代码，注意，一定要带前面的<code>javascript:</code>，有些浏览器由于安全方面的考虑会自动过滤掉这串字符，这时就需要我们手动加上。粘贴好之后按回车，脚本会自动完成选择和提交的动作。默认都选“优”也就是第二档。</p>
<p>还有两种使用方式可以参考：</p>
<ul>
<li>把这段代码加到书签里，到评估页面后直接点书签就行了</li>
<li>在浏览器开发者工具的 Console 中执行</li>
</ul>
<h2 id="python-版最推荐">Python 版（最推荐）</h2>
<p>如果你用的是 Linux/OSX，那么在安装完<code>BeautifulSoup</code>后就可以直接调用了。</p>
<p>BeautifulSoup 安装命令：<code>sudo pip install beautifulsoup4</code>或<code>sudo easy_install beautifulsoup4</code></p>
<p>运行脚本：<code>python pingjiao.py</code></p>
<p>运行效果大概就是下面这样</p>
<p><img src="/img/bupt-pingjiao/cli.png" alt="脚本运行效果"></p>
<h2 id="py4win-版src-推荐exe-非常不推荐">Py4win 版（src 推荐，exe 非常不推荐）</h2>
<p>如果你在 windows 上装了 python，那么使用方法和 Python 版完全相同，装好 BeautifulSoup 就能用了。</p>
<p>如果没装，但是你还非常想用的话，那只能用 exe 版本了。exe 版本使用非常简单，双击“评教.exe”运行就行了。</p>
<p>exe 版本使用 Py4win 版的 src+py2exe 打包，使用上没有任何问题，但是我不推荐用。直接给可执行文件是个很敏感的事儿，你完全可以说我植入了什么奇怪的东西，虽然我什么都没干。所以如果你使用 exe 版的话，你必须<strong>自己承担这带来的一切后果</strong>。有点危言耸听了，但是永远要相信「6L+Z5Liq5LiW55WM5LiK5bCP5Lq66L+Y5piv5oy65aSa55qE」。</p>
<h2 id="所有的文件来这儿下载">所有的文件来这儿下载</h2>
<ol>
<li>源码在 <a href="https://github.com/hansnow/bupt-pingjiao">GitHub</a></li>
<li>Py4win 版的 exe 文件在 <a href="https://pan.baidu.com/s/1c09v6Ru">百度云</a></li>
</ol>
<h2 id="special-thanks">Special Thanks</h2>
<p>感谢 @qiukun 大神的指导。良师益友没得说~</p>
]]></content>
		</item>
		
		<item>
			<title>Hi! 2015!</title>
			<link>https://yunbo.li/posts/hello-2015/</link>
			<pubDate>Sun, 04 Jan 2015 17:38:09 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/hello-2015/</guid>
			<description>&lt;p&gt;时间过得真快，转眼就到 2015 年了。回想起去年的这个时候，我已经住院了。在写这篇日志之前，我又看了看去年在这个时候写的日记，看完之后心里挺不是滋味儿的，当时确实是受了不少委屈。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>时间过得真快，转眼就到 2015 年了。回想起去年的这个时候，我已经住院了。在写这篇日志之前，我又看了看去年在这个时候写的日记，看完之后心里挺不是滋味儿的，当时确实是受了不少委屈。</p>
<blockquote>
<p>既然决定了要住院，我就回学校办手续了，回来那天是 12 月 31 号，我还记得当时走出医院的时候我还琢磨着，老天还是挺够意思的，至少没让我在医院跨年。31 号那天晚上我请我们宿舍的吃饭，花的不多，还不到 300 块钱，特别逗的是隔壁的一个菜上到我们的桌子上了，我们义无反顾的就给吃了。虽然吃的不丰盛，但是大家还算高兴。那天晚上，大家似乎也没有太激动，0 点，我站在阳台上带头喊了一句：新年快乐。当时百感交集，新年了，这一年的开头就这么累。免不了有点哀伤。</p></blockquote>
<p>但是住院的那段日子，我真的很快乐。</p>
<blockquote>
<p>不得不承认，住院的这二十天是我今年最爽的日子了。无忧无虑，我从没有像现在这么友善过，也从没有像现在这么正能量过，我见每个人的时候都微笑，见每个稍微认识点的人都热情的打招呼，关键是，我是发自内心的，我发自内心的和订饭的阿姨开玩笑，发自内心的觉得应该送何叔叔点礼物，发自内心的对对面屋的姑娘说声：“就不！”。</p></blockquote>
<p>这就是 14 年的开头，在这段开头的时间，我写了 20 年来几乎最励志的日记。</p>
<p>当然，整个 2014 年还发生了很多有意思的故事，最大的一件事儿就是实习了。今天就不说了吧，该期末复习了。</p>
<p>接下来这一年肯定还会发生好多故事，不求别的，只求能离自己的心近一点。</p>
]]></content>
		</item>
		
		<item>
			<title>又是一个月，好久不见</title>
			<link>https://yunbo.li/posts/long-time-no-see-at-the-end-of-2014/</link>
			<pubDate>Fri, 05 Dec 2014 20:18:40 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/long-time-no-see-at-the-end-of-2014/</guid>
			<description>&lt;p&gt;啊，又一个月没来了。一转眼又到了年末，想起去年这个时候，痛苦就要来了，我当然不想让它来。过得真快，一转眼一年又过去了。&lt;/p&gt;
&lt;p&gt;今年刚刚开始，我去车道沟“旅行”，当时心情压抑的不行，同时还有点庆幸，让我在宿舍跨了年。“旅行”期间想了好多事儿啊，当时一点一点地写在了为知笔记里面，而且当时还立志今年一定要好好练练文笔。一整年就快过去了，文笔依然差，写东西依然不勤快。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>啊，又一个月没来了。一转眼又到了年末，想起去年这个时候，痛苦就要来了，我当然不想让它来。过得真快，一转眼一年又过去了。</p>
<p>今年刚刚开始，我去车道沟“旅行”，当时心情压抑的不行，同时还有点庆幸，让我在宿舍跨了年。“旅行”期间想了好多事儿啊，当时一点一点地写在了为知笔记里面，而且当时还立志今年一定要好好练练文笔。一整年就快过去了，文笔依然差，写东西依然不勤快。</p>
<p>人一到某个阶段的末尾就禁不住回忆，今年经历了好多事儿啊。尤其是年中开始去某小公司实习的经历，让我成长了很多。见识了甲方乙方勾心斗角争夺利益，见识了公司里让人恶心的不行的踢皮球现象。网站的项目磨磨唧唧弄到现在，还好有光光，足够强势，一直以来让我们保持主动权，不至于被公司欺负得太厉害。还有就是感恩，感恩这个公司让我认识了这么多好玩儿的同事，大家每天在办公室里逗比的不行，真是开心。</p>
<p>因为做这个项目，迫使自己学了很多新的技术，这种感觉真的是好久都没有过了。想起初中的那段时间，刚刚接触互联网，看什么东西都觉得新鲜，什么都想玩儿。那是个时候非常痴迷“偏门技术”，比如 Windows 下的破解、外挂和黑客技术。现在想想那时候看的东西确实是挺入门的，但那时候学习的热情实在是高的不能再高了，每天脑子里想得都是这些东西。那时候老妈一直不理解，觉得我就是纯粹地在玩儿电脑（这么想也没啥不对，我确实就是在玩儿啊），三番五次地阻止我用电脑，还把电脑设了密码。也就是那时候起，我熟练地掌握了 Ghost 装系统的技术。把原来带密码的系统备份一下，换成自己的没密码系统，等老妈快下班的时候再赶紧换回来。现在想想还挺怀念的。对于那段时间的瞎折腾几乎没有后悔，除了一点，那时候因为要学破解和外挂，稍微学了点汇编的皮毛，没有深入学习。如果那时候的我知道大学还要学一门叫做“微机原理”的课的话，估计汇编早就学成了。</p>
<p>V2EX 是个好社区，和 HiPDA 一样是个人站点。在 ifanr 看了一篇 Livid 的专访，感觉是个十分有意思的人。很多时候，读一些人的访谈或者事迹的时候不禁感慨，真牛逼。回头单独写写吧。zhuyi 成了专业吃货，没事儿的时候敲敲鼓。Livid 不知道在干啥，偶尔会看见他和 Oliva 在社区里互动一下。阮一峰入职阿里了，不过他的博客看得确实不多。伞哥前段时间在微博上和老婆撕逼，嚷嚷着要离婚，哎，过日子真不容易。各位大佬活得有声有色，看他们的故事比看电视剧有意思得多。</p>
<p>晚上很没骨气地去吃了烤肉大哥，之所以说自己没骨气是因为他家现在有外卖了，而店里还是那几个人，还是那么大地方。很明显的，大哥已经有点顾不上店里的顾客了，肉的味道也不怎么好了。之前因为这个已经把他家加入黑名单了，但是今天实在是有点想吃，所以还是很没骨气的去了，果然还是黑名单的命，彻底再见了。今天听大哥说，他这个店已经开了 11 年，真的，因为外卖毁了真的不太好。想起前些日子去南门最里面的那家北邮学长开的黄焖鸡，像叔叔熊一样，被悄无声息的收购掉了。有些嘴比较叼的顾客从味道上就能尝出来，还有些顾客从店员的变化里看出来了。总之以后是不会去了。想到这里心里还有点不是滋味，南门能吃的店越来越少了，以后去哪儿吃饭啊，发愁……</p>
<p>踏踏实实活着，老老实实攒钱，还信用卡，等待圣诞节割肉。还有，我啥时候能捡个女朋友啊，愁……</p>
<p>哦，晚上光光告诉我爱知乎关了。挺好的站点，因为版权问题不得已关掉了。惆怅……不过目前还好，RSS 还没停。依旧惆怅……</p>
]]></content>
		</item>
		
		<item>
			<title>当我再一次谈论起奋斗时，我在想些什么</title>
			<link>https://yunbo.li/posts/what-do-i-think-when-i-talk-about-fighting/</link>
			<pubDate>Tue, 04 Nov 2014 10:04:20 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/what-do-i-think-when-i-talk-about-fighting/</guid>
			<description>&lt;p&gt;最近身边的人多多少少都有点不正常，有的忙着平时的作业、实验等等琐碎的学业，有的里里外外跑来跑去，忙着各种高大上的事业。总之，大家都忙。忙着忙着很多人就会想，我这么瞎折腾是图什么呢？这么做有什么意义吗？想到这儿，一部分人颓了，不折腾了，开始“享受”生活。另一部分人想到了奋斗，他们觉得这是一种向上的生活状态，于是咬咬牙继续拼。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>最近身边的人多多少少都有点不正常，有的忙着平时的作业、实验等等琐碎的学业，有的里里外外跑来跑去，忙着各种高大上的事业。总之，大家都忙。忙着忙着很多人就会想，我这么瞎折腾是图什么呢？这么做有什么意义吗？想到这儿，一部分人颓了，不折腾了，开始“享受”生活。另一部分人想到了奋斗，他们觉得这是一种向上的生活状态，于是咬咬牙继续拼。</p>
<p>说起奋斗，我脑海里最深刻的记忆应该就是高中的那段时间了吧。其实高中的故事我实在是不想再提，不管是自己回忆还是和别人讲述，都已经讲得太腻味了。无非就是早上五点起、晚上十一点半睡、一个月放两天假等等等等……没啥可说的了，我知道很多当时的高考考生比我努力的多。</p>
<p>但是我还是想说说这事儿。国庆节放假回家，在老房子里翻到了高三时的笔记本，里面零零散散的记着一些错题和知识点，这不奇怪。比较让我纳闷儿的是每隔几页就会有一些给自己洗脑的话。比如明天会更加辉煌啊之类的，我实在是想不出当时怎么会这么相信“成功学”，自己哪儿来的文采给自己注射了这么多鸡血。说真的，照那个思路写下去，大学毕业之后当个“成功学”作家或者进中宣部肯定没问题。</p>
<p>上大学之后全变了。来了北京之后见到了很多奇奇怪怪的东西，眼界开阔了，变得越来越不会轻易相信某些东西。对很多东西的认识也在被自己不断地推翻。就拿奋斗这件事儿来说，过去的时候我觉得奋斗嘛，只要咬着牙拼命去做就是了。现在想想，真是 naive 啊。回想自己上高中的时代，真是因为所谓的“奋斗”伤了不少人。当时很多人让我帮着讲题，我都态度恶劣的回绝了。班里的事儿也是能不掺和就不掺和。说实话，要不是当时我成绩不错，早就在班里混不下去了。所以我很明白，过去那种“奋斗”的方式不对，首先对你自己来说就很不舒服，确实，自己做完了很多事儿，但同时呢，你发现身边的同学离你越来越远。本该放松下来大家说笑打闹的时候，人家再也不会叫上你。如果这时候你想着：奋斗的人总是孤独的。那么好，你完蛋了。对别人来说，你的“奋斗”伤害了他们。让他们觉得你无情冷漠，同学朋友的友谊在你眼里一文不值。</p>
<p>现在，我觉得奋斗应该是这样的。做什么事儿，安安静静的做就好，遇到瓶颈了别哭天抢地，取得一点成绩的时候也别沾沾自喜。现在很多场合都在谈论“热情”。“拿出你们的热情！”——别了吧，我看还是拿出靠谱的计划比较好。热情只是一时的，只有长久稳定的计划才能为执行力提供保障。</p>
<p>唔&hellip;实在是写不下去了。为了凑够 800 字，下面全是扯淡。</p>
<p>现在坐在图书馆里敲字，深秋的上午，阳光明媚，而且没有最讨厌的瑟瑟秋风，外面的树动都不带动一下的。世界从来没这么安静过，每个人都静静地做自己的事儿。回想起昨天宋艳杰的事儿，我还真是觉得，活着真好。</p>
]]></content>
		</item>
		
		<item>
			<title>吸着雾霾去跑马</title>
			<link>https://yunbo.li/posts/2014-marathon/</link>
			<pubDate>Thu, 30 Oct 2014 10:14:06 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/2014-marathon/</guid>
			<description>&lt;p&gt;欠了好久的文章，今天总算是得空给写了。话说今年是第二年参加北京马拉松，记得去年马拉松的时候，和宿舍两个人一起去参加，3 万人一起在马路上狂奔的感觉真心赞。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>欠了好久的文章，今天总算是得空给写了。话说今年是第二年参加北京马拉松，记得去年马拉松的时候，和宿舍两个人一起去参加，3 万人一起在马路上狂奔的感觉真心赞。</p>
<p>本来今年也是抱着狂欢的心态去的，结果遭遇了各种不爽。真是不得不上来吐槽下了。</p>
<h3 id="雾霾啊雾霾">雾霾啊，雾霾！</h3>
<p>马拉松前些天大家就得到了不幸的消息：马拉松当天会有严重雾霾。当时很多人就表示，如果当天污染指数达到了 300 就弃赛。当天五点半起床的时候往窗外看，已经是雾蒙蒙一片了。果不其然，当天的污染指数轻轻松松的破了 300。可就算是这样，参加比赛的人依然非常非常多。存衣服、做热身依旧是挤挤挤！</p>
<h3 id="到处都是撒尿的">到处都是撒尿的</h3>
<p>去年马拉松完了之后，新闻上的头条是关于比赛选手到处撒尿的。说实话去年我还真没觉得，但是今年我算是领教了。跑到蓝靛厂南路、北路的时候路边到处都是沿着河边撒尿的，真是服了……</p>
<h3 id="成绩不能更丢人">成绩不能更丢人</h3>
<p>去年两小时五十分勉勉强强跑完了半程。今年跑之前琢磨着，参加测向比赛之前也算是训练了，成绩应该不会比去年差吧。可是我忽略了疯长的体重，和去年比，体重飙升了十几公斤，这些重量足够我喝一壶了。跑到 17 公里的时候腿就抽了，尝试着挣扎了一下，结果还是一跑就抽。无奈，只能退赛了。这一退赛可算是让我遇到了各种奇葩事儿。</p>
<h3 id="路边惊魂">路边惊魂</h3>
<p>刚才说到，17 公里的时候我腿抽筋了，抽筋了就上收容车呗。上了收容车，人家说还没到时间呢，得等会儿才能开。于是我就坐在车上到处望。结果一回头，看见一位男选手在追着一位女选手打闹，当时心里想的是男女朋友也别在这儿打闹啊。结果那男的追上女的之后直接抱住就啃，注意，是真啃。旁边的选手马上就把男的拉开了，结果那男的就躺在地上抽……太特么吓人了。当时收容车上的志愿者就打了急救电话。一会儿收容车开走了，也不知道后来是怎么处理的。</p>
<h3 id="正文">正文</h3>
<p>其实主线故事是在收容车开走之后才开始的。</p>
<p>收容车开动的时候 17 公里处已经快要关门了，路上已经有了其他车辆，特别堵，所以师傅开起来本来就不爽。就这么着蹭到了北四环的学院桥。大家都知道，学院桥往东一拐就是奥体也就是终点了。于是师傅跟车上的志愿者说咱直接回奥体吧。志愿者肯定不能同意呀，按规定收容车肯定是要沿着赛道开回去的，毕竟前面还会有很多中途退赛的选手的。于是师傅就开始骂骂咧咧的，这时候车上的人也基本分成两拨了，一拨人觉得既然都离得这么近了赶紧回去就得了，另一拨人就说你上车了当然没事儿，前面还有那么多人没上车呢，他们怎么办？然后两拨人就开始吵。后来师傅直接急眼，就差动手了……我真服了，有这个劲儿干嘛不再跑一会儿啊，刚上车的时候一个个都快死了的样子，上来之后一下子都来了精神。当时我是真的怒了，但实在是没劲儿说话。</p>
<p>后来师傅还是妥协了，听了志愿者的话，按照规定继续往前走。可是刚走了没一会儿，车上的志愿者接到组委会的电话，说时间太晚了，收容车可以直接开回终点了。听到这个消息，司机师傅直接就炸了，“早就说直接回去就得了，这回好吧，学院桥过了，你说我怎么拐回去。行了，堵着吧！”。一会儿终于又从北往南开回了学院桥，可是得往西走一段。这师傅司机又开始赌气了，玩命儿往西开，就是不掉头，边开边说：你们不是指挥我么，这回你们让我往东开，我偏往西开。全车人无语，只能等师父解气了再说了。</p>
<p>一会儿师父终于玩儿爽了，掉头往东开。结果到了奥体之后已经来晚了，人家已经不给收容车开门了。于是又绕着奥体公园转转转，终于在大屯那边找到了一个入口。进了公园没一会儿保安把我们拦下说不能往里开了。大家只好“被下车”。下车之后大家准备去终点处取衣服，可是被拦在玲珑塔下面了，那个门死活也不给我们开，说只有工作人员能进去。在沟通的过程中我还和门口保安发生了一点小小的语言冲突。说了半天人家就是不让进，没办法，大家准备绕个大圈看看能不能从另一面进去了。刚走没一会儿，门开了！当时心里一亿只草泥马奔腾而过啊！这尼玛干啥呢，玩儿我们呢啊？有完没完？</p>
<p>没完！刚过了玲珑塔关卡，我们又来到了终点关卡。和刚才一样，又是只能工作人员进去。这回大家根本不商量了，因为这次是护栏。过去北京的交通护栏是 30cm 的，现在是 1.5m 的。所以你懂的，根本没人商量，直接就翻了。保安一看翻护栏的人实在是太多，不得已把门就给开了。至此，我终于找到了衣服。赶紧取出装衣服袋子里的香蕉和巧克力一口气吃了，然后坐在旁边的石头墩子上吹冷风。啊，终于结束了！</p>
<p>做梦！刚坐下就开始给一起来的两个小伙伴打电话，结果一个小伙伴的手机没电关机了。另一个小伙伴因为方向搞错了半天过不来。我又一次跪倒在地上，这特么是上天对我的惩罚么。在第二个小伙伴继续摸索走过来的路线的时候，我继续坐在石头敦子上吹冷风，顺便思考人生。这次马拉松真不爽啊，雾霾这么大，成绩这么差，说好的去半程终点看裴帅去矿大看楠姐的目标都没达成。上了收容车之后看一车人分成三拨吵架。哎，气过头了，好想哭。</p>
<p>哦！想起来了，和一个小伙伴汇合之后一起坐公交回来。公交车是下车刷卡的那种，我下车的时候还刷了卡。就这样，到宿舍之后发现公交卡丢了……里面还有九十大洋！已经欲哭无泪了。</p>
<h3 id="哎人生好艰难-">哎，人生好艰难 ╮(╯▽╰)╭</h3>
<p>这就是我的 2014 年马拉松，人生第二次马拉松，第一次全马。很遗憾没完赛，不过明年我会继续跑。从现在起会一直为 2015 北马准备哦！这次要特别感谢北邮跑团的小伙伴们，尤其是 Five 姐姐，还专门给做了 BUPT 的 logo，简直赞。大家明年一起加油哦！</p>
]]></content>
		</item>
		
		<item>
			<title>北邮人BT“说谢谢”刷分</title>
			<link>https://yunbo.li/posts/byrbt-thanks-cheater/</link>
			<pubDate>Wed, 15 Oct 2014 09:36:37 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/byrbt-thanks-cheater/</guid>
			<description>&lt;h1 id=&#34;前言&#34;&gt;前言&lt;/h1&gt;
&lt;p&gt;今天上午刚刚搬家到新的博客，这个博客使用 Markdown 语法写作，靠 DropBox 同步文章，感觉还是有点儿意思呢。之前的 Octopress 博客&lt;a href=&#34;http://coding.yunbo.li&#34;&gt;http://coding.yunbo.li&lt;/a&gt;访问速度实在是太差，而且到现在为止我还是没学会怎么在上面发布文章，这时 Farbox 出现了，索性转了过来。btw，以前的负能量小站已经迁移到了&lt;a href=&#34;http://laodao.yunbo.li&#34;&gt;http://laodao.yunbo.li&lt;/a&gt;&lt;/p&gt;</description>
			<content type="html"><![CDATA[<h1 id="前言">前言</h1>
<p>今天上午刚刚搬家到新的博客，这个博客使用 Markdown 语法写作，靠 DropBox 同步文章，感觉还是有点儿意思呢。之前的 Octopress 博客<a href="http://coding.yunbo.li">http://coding.yunbo.li</a>访问速度实在是太差，而且到现在为止我还是没学会怎么在上面发布文章，这时 Farbox 出现了，索性转了过来。btw，以前的负能量小站已经迁移到了<a href="http://laodao.yunbo.li">http://laodao.yunbo.li</a></p>
<p>目前这个系统的大体流程已经搞明白了，但是和其他博客系统一样，主题修改还是让我头疼。</p>
<p>不过也管不了那么多啦，等把一切都搞明白了再开始写不知道要等到什么时候了，索性就这样写吧，反正文章丢不了。</p>
<p>今天给大家带来的是一个 Python 写的小玩具，在北邮人 BT 上说谢谢刷魔力值。</p>
<p>在北邮人 BT 上有这么一条规则：</p>
<p><img src="/img/byrbt-thanks-cheater/rule.png" alt="BT上通过说谢谢得到魔力值的规则"></p>
<p>而且除了对每个种子只能说一次之外，系统对说谢谢这个功能没有其他限制。BYRBT 上大概有 4W 个种子，也就是说我们可以通过说谢谢得到 4W 魔力值。我们要做的就是靠 Python 来实现说谢谢的功能从而获得魔力值。</p>
<p>好，了解完思路，让我们进入实战环节。</p>
<h1 id="说谢谢背后的原理">说谢谢背后的原理</h1>
<p>当我们对一个种子“说谢谢”的时候，浏览器干了什么？</p>
<p>首先，我们打开浏览器（以 Chrome 为例了，据说 FireFox 的 FireBug 也很好用），进入 BT 随便打开一个种子，比如我们打开这个<a href="http://bt.byr.cn/details.php?id=152137&amp;hit=1">http://bt.byr.cn/details.php?id=152137&amp;hit=1</a>，页面拉到种子信息那里，我们可以看到说谢谢按钮。</p>
<p><img src="/img/byrbt-thanks-cheater/thanks_button.png" alt="说谢谢按钮"></p>
<p>这时，请出我们今天的第一个大杀器——Chrome 开发者工具[^1]。</p>
<p>Chrome 开发者工具是随 Chrome 浏览器一起发布的面向开发人员的网页调适工具，通过它，我们可以看到网页背后的技术细节。在 Windows 下，我们可以用<code>F12</code>打开开发者工具，在 OSX 下，快捷键为<code>Command+Option+I</code>。</p>
<p>打开开发者工具后，我们会开到这样一个窗口。最上方是功能选项卡，不同的选项卡下对应着不同的功能，我们用的比较多的是<code>Elements</code>和<code>Network</code>选项卡。在<code>Elements</code>选项卡中，我们可以方便地以标准的树状结构浏览 HTML 元素，通常这对了解网页布局和网页元素的细节是很有帮助的。</p>
<p><img src="/img/byrbt-thanks-cheater/dev_tool.png" alt="Chrome开发者工具"></p>
<p><code>Network</code>选项卡主要用来显示网页背后的数据交互，也就是用来查看 HTTP 请求。这里有两个按钮需要注意，首先是左上角的那个红点，它的左右和录音机的录制键差不多，红色表示记录状态，灰色表示非记录状态。它旁边的那个灰色叉子按钮是 Clear 键，用来删除已有的记录。在这一行的最右侧是<code>Preserve log</code>选项，如果它处于选中状态，那么即使网页刷新或者跳转，已有的记录依然会被保留。（默认情况下一旦页面刷新，已有记录会被清除）</p>
<p><img src="/img/byrbt-thanks-cheater/dev_tool_highlight.png" alt="Network标签中两个比较重要的按钮"></p>
<p>现在我们要用到<code>Network</code>功能，所以切换到<code>Network</code>选项卡。然后，我们点击“说谢谢”按钮。我们观察到，<code>Network</code>选项卡下出现了一个条目：</p>
<p><img src="/img/byrbt-thanks-cheater/request.png" alt="关键POST出现"></p>
<p>这里我们需要关注两个点，首先是<code>Path</code>==&gt;<code>thanks.php</code>。然后是<code>Method</code>==&gt;<code>POST</code>，也就是说，在我们点“说谢谢”按钮的时候，实际上是向<code>thanks.php</code>发送了一个<code>POST</code>请求[^2]。</p>
<p>点击这个 POST 请求查看详情，注意框框中的内容<code>Request URL:http://bt.byr.cn/thanks.php</code>和<code>id:152137</code>。第一个是我们发送 POST 请求的 URL，也就是说，是这个 URL 接受了浏览器发送的请求；第二个是请求过程中发送的参数（表），这里也就是种子 ID（还记得我们最开始打开了哪个 URL 吗？）</p>
<p>到此，我们已经清楚的了解了在点击“说谢谢”按钮时，浏览器都做了哪些事儿。那接下来就是用 Python 模拟这个动作了。</p>
<h1 id="用-python-实现">用 Python 实现</h1>
<p>让我们整理一下思路，说谢谢的核心就是一个 POST 请求，这个请求有两个变量，<code>Request URL</code>是固定的，<code>id</code>是种子 ID。也就是说只要我们得到多少种子 ID 也就会得到相应的魔力值。那么接下来我们要做的就是抓取全站的种子 ID。</p>
<p>打开一个<a href="http://bt.byr.cn/torrents.php">种子页面</a>，然后右键-&gt;查看网页源代码，我们发现有很多类似这样的代码：</p>
<ul>
<li><code>href=&quot;details.php?id=152186&amp;amp;hit=1&quot;</code></li>
<li><code>href=&quot;viewsnatches.php?id=152186&quot;</code></li>
<li><code>href=&quot;comment.php?action=add&amp;amp;pid=152186&amp;amp;type=torrent&quot;</code></li>
<li><code>href=&quot;download.php?id=152186&quot;</code></li>
</ul>
<p>这里面都包含了我们需要的种子 ID<code>152186</code>，所以方法就有了，只要通过正则表达式将网页源码中的种子 ID 匹配出来就可以了。</p>
<p>于是，我们祭出今天的第二个大杀器——BeatutifulSoup。</p>
<blockquote>
<p>Beautiful Soup 是用 Python 写的一个 HTML/XML 的解析器，它可以很好的处理不规范标记并生成剖析树。通常用来分析爬虫抓取的 web 文档。对于 不规则的 Html 文档，也有很多的补全功能，节省了开发者的时间和精力。</p></blockquote>
<p>简单说，BeautifulSoup 的作用是让我们更方便的在 HTML 代码中找东西。</p>
<h2 id="材料已备齐开搞">材料已备齐，开搞</h2>
<p>从这里开始我们按事件发生的先后顺序讲……要不然我怕讲乱了……</p>
<h2 id="登录">登录</h2>
<p>登录是以后所有操作的基础，所以我们先看看登录的过程。打开 Chrome 开发者工具，找到 Network 选项，把 Preserve log 选中。然后我们登录一下 BT</p>
<p><em>很不幸，BT今天挂掉了，截图只能以后搞了</em></p>
<p>我们可以看到，登录的 POST 请求中发出了四个数据 username、password、imagestring 和 imagehash。前两个不用说，是你的用户名密码。imagestring 是你输入的验证码字符，imagehash 是与这张验证码图片对应的一个 hash 值，只有 hash 值和你输入的 string 值成功匹配时你才能登录。</p>
<p>好像说着说着就有点罗嗦了，有空慢慢写吧……to be continued</p>
]]></content>
		</item>
		
		<item>
			<title>Ubuntu下的LAMP和vsftpd配置</title>
			<link>https://yunbo.li/posts/lamp-and-vsftpd-setup/</link>
			<pubDate>Fri, 10 Oct 2014 09:31:08 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/lamp-and-vsftpd-setup/</guid>
			<description>&lt;h2 id=&#34;写在前头&#34;&gt;写在前头&lt;/h2&gt;
&lt;p&gt;很难想象我竟然起了一个这么 ordinary 的 tilte，事实上标题里说的这个问题已经虐了我一个晚上，尤其是 vsftpd，当初在树莓派上配置过 vsftpd，隐约记得没有多长时间就配好了。结果隔了半年再次尝试 vsftpd 的时候，这货狠狠地抽了我一巴掌，死活配不对，尤其是用户那一块儿，简直烦死了。好不容易折腾好了，马克一下，防止下次被虐。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<h2 id="写在前头">写在前头</h2>
<p>很难想象我竟然起了一个这么 ordinary 的 tilte，事实上标题里说的这个问题已经虐了我一个晚上，尤其是 vsftpd，当初在树莓派上配置过 vsftpd，隐约记得没有多长时间就配好了。结果隔了半年再次尝试 vsftpd 的时候，这货狠狠地抽了我一巴掌，死活配不对，尤其是用户那一块儿，简直烦死了。好不容易折腾好了，马克一下，防止下次被虐。</p>
<h2 id="环境说明">环境说明</h2>
<p>写这篇文章的时候没在学校，手上只有一台弱不经风的 air，小心翼翼地在虚拟机里装了 Ubuntu Server 12.04。网卡用了桥接模式，所以虚拟机里的系统得到的是类似 192.168.1.xxx 这样的 IP。其他的就没什么好说的了。</p>
<h2 id="先换个源">先换个源</h2>
<p>北京地区的推荐北京交通大学或者搜狐的源，速度超快哦~如果你在教育网的话那就随意了，清华、北理工、华中科大等等速度都好得不得了。</p>
<p>以北交源为例，从<a href="http://mirror.bjtu.edu.cn/cn/howto.html">这个页面</a>查看源地址，然后修改 sources.list。</p>
<p>修改之前，先备份一下</p>
<pre tabindex="0"><code>sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
</code></pre><p>然后放心大胆地修改 sources.list 即可</p>
<pre tabindex="0"><code>sudo vim /etc/apt/sources.list
</code></pre><h2 id="lamp-安装">LAMP 安装</h2>
<p>LAMP 是指 Linux、Apache、MySql 和 PHP。安装过程很简单，麻烦的是配置。首先在终端里运行下面的命令进行 LAMP 的安装</p>
<pre tabindex="0"><code>sudo apt-get install apache2 mysql-server mysql-client php5 php5-gd php5-mysql
</code></pre><p><strong>安装过程中需要设置 mysql 的 root 密码，这个很重要，请千万记牢！</strong>
<img src="/img/lamp-and-vsftpd-setup/set-password.png" alt="设置mysql的root密码">
由于 LAMP 大部分操作与/var/www 目录相关，为了方便，修改该目录的权限为普通用户可访问。</p>
<pre tabindex="0"><code>sudo chmod 777 /var/www/
</code></pre><h2 id="phpmyadmin-的安装">phpmyadmin 的安装</h2>
<p>phpmyadmin 可以让我们方便的通过 web 管理服务器上的 mysql 数据库。</p>
<pre tabindex="0"><code>sudo apt-get install phpmyadmin
</code></pre><p>在安装过程中会要求选择 Web server：apache2 或 lighttpd，选择 apache2
<img src="/img/lamp-and-vsftpd-setup/choose-server.png" alt="选择服务器类型">
然后会要求输入一个密码，建议和 mysql 的 root 密码设置成一样的。
<img src="/img/lamp-and-vsftpd-setup/phpmyadmin.png" alt="配置phpmyadmin">
然后把 phpmyadmin 的目录链接到/var/www 里面</p>
<pre tabindex="0"><code>sudo ln -s /usr/share/phpmyadmin /var/www
</code></pre><p>到此，LAMP 和 phpmyadmin 的安装就完成了，这时候在浏览器中输入虚拟机的 IP 地址，可以看到“It works！“的提示，说明 Apache 已经正常工作了。进入 phpmyadmin 目录，就像这样 192.168.1.xxx/phpmyadmin，就可以看到 phpmyadmin 的首页了，用户名 root，密码用前面几步输入的 root 密码就可以登录啦。
<img src="/img/lamp-and-vsftpd-setup/phpmyadmin-login.png" alt="phpmyadmin登陆界面">
<img src="/img/lamp-and-vsftpd-setup/phpmyadmin-main.png" alt="phpmyadmin主界面"></p>
<h2 id="万恶的-vsftpd">万恶的 vsftpd</h2>
<p><em>本来是想弄成多用户的，每个用户只能操作自己目录内的文件，但是操作的时候遇到了一些问题，waiting for update</em>
我们的应用场景是有多个 FTP 用户，分别对应着不同的 Home 目录，并且彼此只能在各自的目录内进行操作。</p>
<p>先安装</p>
<pre tabindex="0"><code>sudo apt-get install vsftpd
</code></pre><p>然后我们建立一个 ftp 用户，比如叫 ftpuser</p>
<pre tabindex="0"><code>sudo useradd ftpuser
sudo passwd ftpuser
</code></pre><p>为 ftpuser 建立用户目录</p>
<pre tabindex="0"><code>sudo mkdir /home/ftpuser
</code></pre><p>改变权限</p>
<pre tabindex="0"><code>sudo chown -R ftpuser /home/ftpuser
</code></pre><p>用户部分处理完，我们开始配置 vsftpd</p>
<pre tabindex="0"><code>sudo vim /etc/vsftpd.conf
</code></pre><p>参考下面的配置</p>
<blockquote>
<p>#禁止匿名用户登录</p></blockquote>
<pre><code>anonymous_enable=NO
#本地用户可用
local_enable=YES
#可用写操作
write_enable=YES
#权限掩码(设置上传的文件为所有用户可读写,这样Apache就有读写权限了)
local_umask=000
#不须要显示某目录下文件信息
dirmessage_enable=NO
#加点登录后的提示语句
ftpd_banner=Hello~~
</code></pre>
<p>重启 vsftpd，上面的配置就生效啦~</p>
<pre tabindex="0"><code>sudo /etc/init.d/vsftpd restart
</code></pre><h2 id="参考文档">参考文档</h2>
<ol>
<li><a href="https://www.linuxidc.com/Linux/2012-05/61079.htm">Ubuntu 12.04下LAMP安装配置</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>记途中的一对情侣</title>
			<link>https://yunbo.li/posts/young-couple-in-2012/</link>
			<pubDate>Mon, 06 Oct 2014 21:31:35 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/young-couple-in-2012/</guid>
			<description>&lt;blockquote&gt;
&lt;p&gt;国庆假期回家，在自己的房间里翻出来高中时候的笔记本。翻了翻，发现了一些幼稚的文字。发上来留个纪念吧。下面这篇应该是 2012 年 3 月的。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;3 月中旬，我去外地考试，在回家的班车上看见了一对情侣，他们是那么朴素，自然，衣着也并不华丽。女生脸上有淡淡的高原红，她话不多，一举一动都带着几分害羞。男生显得高大，结实。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<blockquote>
<p>国庆假期回家，在自己的房间里翻出来高中时候的笔记本。翻了翻，发现了一些幼稚的文字。发上来留个纪念吧。下面这篇应该是 2012 年 3 月的。</p></blockquote>
<p>3 月中旬，我去外地考试，在回家的班车上看见了一对情侣，他们是那么朴素，自然，衣着也并不华丽。女生脸上有淡淡的高原红，她话不多，一举一动都带着几分害羞。男生显得高大，结实。</p>
<p>这对情侣带着两只大大的旅行箱，看得出已经在旅途上奔波了好久。上车后，由于人多，他们不得不分开坐，女生坐在班车的中间位置，后背朝前，正好面向我。男生坐在了大后面。此时女生显得十分不安，脸上没有了笑意，并开始不断的咬嘴唇。她变得更加少言，买票的问她问题，她也只是配合着手势简单的回答。她就这样沉默了好久。这时有几个乘客要下车，男生终于挤到了前面，可是座位还是不够，所以女生坐在了他怀里。</p>
<p>雨停了，花朵张开笑脸，对着太阳微笑。女生终于笑了，她笑得那样自然，亲切。也许对她来说，只有和那男生在一起时，才有安全感，这就是爱，一份正经历着奔波洗涤的爱。看得出，他们俩并不富裕，但他们的这份爱却值得所有人羡慕，就像樱花的粉色，让人感到无比舒适。</p>
<p>我的文笔差到了极点，一件无比美好的事被我描写成这样。不管将来如何，我希望他们此时此刻正体会着对方浅浅的爱。我还年少，经世不多，我深知自己没有能力去评价什么是爱，但内心告诉我，这就是爱。</p>
]]></content>
		</item>
		
		<item>
			<title>唠叨，从这里开始</title>
			<link>https://yunbo.li/posts/a-new-start/</link>
			<pubDate>Wed, 10 Sep 2014 17:03:38 +0800</pubDate>
			
			<guid>https://yunbo.li/posts/a-new-start/</guid>
			<description>&lt;p&gt;最近一周接触了很多 javascript 的东西，从最开始的基本 javascript 语法，到 jQuery，然后是 coffeescript，最后到了 node.js。昨晚看到了一个基于 nodej.s 的项目——hexo，当即被它漂亮的外观吸引。折腾到今天下午，终于算把博客搭建起来了。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>最近一周接触了很多 javascript 的东西，从最开始的基本 javascript 语法，到 jQuery，然后是 coffeescript，最后到了 node.js。昨晚看到了一个基于 nodej.s 的项目——hexo，当即被它漂亮的外观吸引。折腾到今天下午，终于算把博客搭建起来了。</p>
<p>这个小站会取代过去的 laodao.yunbo.li，作为我矫情、发牢骚的地方。小站虽然搭建在 Github Pages 上，但是前端库文件都是用的 BaiduCDN，所以速度应该还说得过去。今天晚上准备把 laodao.yunbo.li 给停掉了，又有一段记忆要被尘封。</p>
<p>感觉大二这一年还是让我成长了不少，至少让我在遇到那么多事儿的情况下不那么心慌了。搁过去，这么多事儿压在我身上的话，估计早就焦虑得睡不着觉了。虽然现在还是会因为这些琐事头疼，但还好，不会再像过去一样——心里波涛汹涌，翻江倒海。</p>
<p>过去的一段时间以“忙”为理由推掉了好多好多事儿，比如健身，比如写博客，比如读书。希望从这个小站建立开始，我能改掉这个坏毛病吧。昨天跑步的时候得到一个体会，按时间比按量来的舒服，30 分钟一到，我马上停下来，因为我知道还有别的事儿要做，不能咬着牙在这里跑步。做其他事儿也一样，在自己还算舒服的情况下，尽量做就好。时间点到了，马上投身到下一件该做的事儿上。</p>
<p>如果让我用一句诗来描述最近的心态的话，那就是——悟以往之不谏，知来者之可追。</p>
]]></content>
		</item>
		
	</channel>
</rss>
