-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
148 lines (94 loc) · 84.9 KB
/
atom.xml
File metadata and controls
148 lines (94 loc) · 84.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Boelroy</title>
<link href="/atom.xml" rel="self"/>
<link href="http://blog.boelroy.com/"/>
<updated>2018-02-24T18:07:46.408Z</updated>
<id>http://blog.boelroy.com/</id>
<author>
<name>Boelroy</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>数组初始化为0体位的性能测试</title>
<link href="http://blog.boelroy.com/2018/02/25/javascript-array-init-performance/"/>
<id>http://blog.boelroy.com/2018/02/25/javascript-array-init-performance/</id>
<published>2018-02-24T17:26:29.000Z</published>
<updated>2018-02-24T18:07:46.408Z</updated>
<content type="html"><![CDATA[<p>数组应该是在JavaScript最常用的数据结构之一,我们有很多种方式来初始化一个数组。但是 JavaScript 怎样把一个数组的所有元素初始化为0 最有效率?为了得出答案,我们做了一下测试:</p><p>我们将在 Node 下(Node.js 版本 v8.4.0)初始化一个长度为 100 万的数组,来对比他们的性能。<br><a id="more"></a></p><h3 id="不同姿势"><a href="#不同姿势" class="headerlink" title="不同姿势"></a>不同姿势</h3><h5 id="姿势-1"><a href="#姿势-1" class="headerlink" title="姿势 1:"></a>姿势 1:</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>(<span class="params">sizeofArray</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> result = [];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i < sizeofArray; i++) {</span><br><span class="line"> result[i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>版本 1 是我们最常见的做法,先声明一个空数组变量,然后用一个 for 初始化数组的每一位。</p><blockquote><p>未预热: 一次 245<br>预热: 平均 261.9<br>预热: 总共 23571</p></blockquote><h5 id="姿势-2"><a href="#姿势-2" class="headerlink" title="姿势 2:"></a>姿势 2:</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>(<span class="params">sizeofArray</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> result = [];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i < sizeofArray; i++) {</span><br><span class="line"> result.push(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>版本 2 中我们在版本 1 中的 for 循环的下标赋值变成了数组api push。</p><blockquote><p>未预热: 一次 282<br>预热: 总共 25233<br>预热: 平均 280.3666666666667</p></blockquote><h5 id="姿势-3"><a href="#姿势-3" class="headerlink" title="姿势 3:"></a>姿势 3:</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>(<span class="params">sizeofArray</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> result = <span class="keyword">new</span> <span class="built_in">Array</span>(sizeofArray);</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i < sizeofArray; i++) {</span><br><span class="line"> result[i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>版本 3 中 首先我们先定义Array的大小 然后通过下标进行赋值</p><blockquote><p>未预热: 一次 61<br>预热: 总共 5048<br>预热: 平均 56.08888888888889</p></blockquote><h5 id="姿势-4"><a href="#姿势-4" class="headerlink" title="姿势 4:"></a>姿势 4:</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>(<span class="params">sizeofArray</span>) </span>{</span><br><span class="line"> <span class="keyword">new</span> <span class="built_in">Array</span>(sizeofArray).fill(<span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>版本 4 中 首先定义了 Array 的大小,然后通过 fill 进行初始化</p><blockquote><p>未预热: 一次 250<br>预热: 平均 59.422222222222224<br>预热: 总共 5348</p></blockquote><h5 id="姿势-5"><a href="#姿势-5" class="headerlink" title="姿势 5:"></a>姿势 5:</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> (<span class="params">sizeofArray</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> arr = [];</span><br><span class="line"> [].length = sizeofArray;</span><br><span class="line"> <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (i < sizeofArray) {</span><br><span class="line"> arr[i] = <span class="number">0</span>;</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>版本 6 首先定义一定一个长度为0的数组变量,然后赋值给 length 为要初始化的大小,最后用循环初始化每一个数。</p><blockquote><p>未预热: 一次 57<br>预热: 平均 61.355555555555554<br>预热: 总共 5522</p></blockquote><h5 id="姿势-6"><a href="#姿势-6" class="headerlink" title="姿势 6:"></a>姿势 6:</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>(<span class="params">sizeofArray</span>) </span>{</span><br><span class="line"> <span class="built_in">Array</span>.prototype.slice.apply(<span class="keyword">new</span> <span class="built_in">Uint8Array</span>(sizeofArray));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>异类 版本5 我们先定义固定大小的 Uint8Array, 这个Type Array 会首先都初始化为0 之后调用Array原型中的slice将其转化为数组</p><blockquote><p>未预热: 一次 1276<br>预热: 平均 913.7111111111111<br>预热: 总共 82234</p></blockquote><h3 id="结果对比"><a href="#结果对比" class="headerlink" title="结果对比"></a>结果对比</h3><p>具体的结果如下所示,我们可以看到一些很有趣的结果。由于 Version 1 是我们在日常变成中最常见的版本,所以我们以 Version 1 为标准对比其他的版本。</p><p><a href="/assets/pics/array-performance-chart.png"><img src="/assets/pics/array-performance-chart.png" alt="array-init-performance"></a></p><p>首先我们可以看到 Version 2 和 Version 1 相比并没有太大的差距,所以我们认为 Version 2 几乎与 Version 1 的写法等价。带来的稍微增多的时间可能是对 push 函数内部的调用所带来的。</p><p>而 Version 3 和 Version 1 相比带了了四倍左右的性能提升。而写法上的改变仅仅是因为在初始化的时候发生了改变,即 <code>let arr = []</code>(或者写成)<code>let arr = Array()</code> 变成了 <code>let arr = Array(size)</code>。为啥这个小小的改变能够带来如此的性能提升呢(问题一)?如果具有 C 或者 C++ 背景的的同学应该会找到一点头绪。</p><p>Version 3 和 Version 5 基本等价。</p><p>对于 Version 4 中在第一次运行的过程中和 Version 1 保持了差不多的性能,但是在运行了多次之后平均的时间变快了。我们知道 JIT 会对运行的代码进行优化,应该是 Version 4被 JIT 优化了。</p><p>最后当然最慢的是逗逼的 Version 6, 其实这个在性能上的损失是显而易见的,Uint8Array 通过 slice 发生了数据转换。从 TypeArray 转换到了 Array, 而且 0 在 Array 中是一个 64 位双精度二进制的值,而 Uint8Array 中 0 是一个无符号的 8 位整数。</p><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>通过上面的分析,我们总结一下遇到的问题。</p><ol><li><code>let arr = []</code>(或者写成)<code>let arr = new Array()</code> 变成了 <code>let arr = new Array(size)</code>。这个小小的改变能够带来如此的性能提升呢。</li><li><code>arr.length = size</code> 发生了什么</li></ol><h3 id="到底发生了什么?"><a href="#到底发生了什么?" class="headerlink" title="到底发生了什么?"></a>到底发生了什么?</h3><p>要回答上面的问题要如何下手呢?首先我们先要弄清楚数组在构造, 下标赋值,push, fill, slice 的时候干了什么事情。</p><h4 id="ECMA-Spec"><a href="#ECMA-Spec" class="headerlink" title="ECMA Spec"></a>ECMA Spec</h4><p>要具体了解发生了什么事情,我们只能去翻一翻 ECMA 标准中是如何规定这些操作的行为的。具体的可以参阅 </p><p>1.<a href="http://lzw.me/pages/ecmascript/#336" target="_blank" rel="noopener">Array 构造</a></p><p>2.<a href="http://lzw.me/pages/ecmascript/#366" target="_blank" rel="noopener">Array的下标赋值描述如下</a></p><p>3.Array <a href="https://www.ecma-international.org/ecma-262/7.0/#sec-array.prototype.fill" target="_blank" rel="noopener">fill</a> and <a href="http://lzw.me/pages/ecmascript/#349" target="_blank" rel="noopener">push</a></p><p>根据 ECMA 标准,Array 初始的时候<code>let arr = []</code>(或者)<code>let arr = new Array()</code> 与 <code>let arr = new Array(size)</code>没有本质的区别,只是后者的数组实例的 length 的 赋值为 size。但是在写入数组的过程中,我们发现 Version 1 会频繁的修改数组实例的 length 属性,这样势必会带来性能的损失。当然根据经验,这点频繁的修改在实际的过程中可能并不会带来多少的性能损失。那么到底哪里出问题了呢?</p><h4 id="没办法只能看内存了"><a href="#没办法只能看内存了" class="headerlink" title="没办法只能看内存了"></a>没办法只能看内存了</h4><p>所以我们只能从别的方面来想办法。如果有其他语言编程背景,应该会猜测对于 Version 1 在插入的时候可能会频繁的出现数组长度不够,需要扩展的时候。那么我们猜测是频繁的内存分配导致了性能的损失。但是根据上面的 ECMA 的标准,其中并没有说明是否当时应该给分类内存,初始化的不同仅仅是 length 的不一样。所以并不能确定这一点,有可能 Version 3 也并没有分配内存,也需要发生频繁的内存分配(当然,JavaScript 的 Runtime 不会像我这么傻)。如何确定呢?</p><p>所以我们还需要跟准确的分析。所以我们在 Chrome DevTool 分别运行两段代码,在 Performance 的比较如下所示。很明显的看到 Version 1 多发生了两次的 Minor GC。 这就是性能损失的原因。Version 1 中可能初始的内存池比较小,在为数组开辟空间的时候,发生了频繁的内存分配,并且当无法分配的时候就会发生 GC。而 Version 3 预先的为数组留够了空间,从而不会再写入的过程中发生 GC。</p><p><a href="/assets/pics/array-init-gc.png"><img src="/assets/pics/array-init-gc.png" alt="array-init-performance"></a></p><h4 id="是时候回答问题了"><a href="#是时候回答问题了" class="headerlink" title="是时候回答问题了"></a>是时候回答问题了</h4><p>通过上面的分析我们可以回答问题1. 性能的损失主要是在内存的频繁分配和 GC 上。</p><p>而问题2 根据 ECMA Spec 很容易的出来。<code>arr.length = size</code> 在 spec 中只是改变了 length 大小。不过如果 size < length, 会删除 size 之后的元素。如果 size > length, 则会形成一个空洞数组(hole).这个新的 size 会告诉 runtime 预留出空间来。</p><h4 id="最正确初始化姿势"><a href="#最正确初始化姿势" class="headerlink" title="最正确初始化姿势"></a>最正确初始化姿势</h4><p>通过上面的分析我们可以看到如果你要操作大数组,那么最好是预留出空间来。lodosh 的 map 好像就是这么做的。不过通过版本4 可以看到,JIT 经过预热后性能也是最理想的,而且代码长度和可读性也是里面最好的一个版本。</p><p>所以自然如果是 es6 的环境下 <code>new Array(sizeofArray).fill(0);</code> 是初始化的最好选择。</p><p>当然这个上面都是突发奇想来探究这个问题,优化在点滴,但是又常说不要过早优化。这篇文章就当做提供一个思路来分析我们工作遇到的问题。</p>]]></content>
<summary type="html">
<p>数组应该是在JavaScript最常用的数据结构之一,我们有很多种方式来初始化一个数组。但是 JavaScript 怎样把一个数组的所有元素初始化为0 最有效率?为了得出答案,我们做了一下测试:</p>
<p>我们将在 Node 下(Node.js 版本 v8.4.0)初始化一个长度为 100 万的数组,来对比他们的性能。<br>
</summary>
</entry>
<entry>
<title>Flex Spec 翻译</title>
<link href="http://blog.boelroy.com/2016/10/15/javascript/%E4%BB%8B%E7%BB%8D/"/>
<id>http://blog.boelroy.com/2016/10/15/javascript/介绍/</id>
<published>2016-10-14T16:00:00.000Z</published>
<updated>2018-02-23T07:32:56.429Z</updated>
<content type="html"><![CDATA[<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>CSS 2.1 已经定义了四种布局模式:</p><blockquote><p>block 布局,是为了布局文档设计的(design for laying out document)<br>inline 布局, 是为了布局文本设计的<br>table 布局,以为了以表格的形式展示 2D 数据设计的<br>positioned 布局,是为了显示在文档中完成元素的定位而设计的,这种布局不用顾忌其他元素在文档中的位置。</p></blockquote><p>这个W3C的文档介绍了一种新的布局模式,flex layout, 这种布局模式是为了更加复杂的web应用和网页设计的。<br><a id="more"></a></p><h3 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h3><p>Flex layout 从表面上看很像 block layout。但是它缺少许多在 block layout 中用于文本和文档为中心的属性,比如 float and columns. 相反的它拥有许多的简单而且强大的工具用于分配空间和对其元素,这些能力往往是 web 应用和复杂网页需要的。flex 容器的内容:</p><blockquote><p>可以从任意的流方向(flow direction)上被布局(左,右,上,下)<br>可以以相反的顺序来展示或者被重排(比如视觉上的顺序可能独立于源码的顺序,或者语言上的顺序(speech order))<br>可以在主轴上被线性的排列也可以次轴(sencondary axis)被打包成多行<br>可以根据当前可用的空间来伸缩大小<br>can be aligned with respect to their container or each other on the sencondary<br>可以动态的在主轴上收缩或者展开 while preserving the container’s cross size</p></blockquote><h4 id="例子1"><a href="#例子1" class="headerlink" title="例子1"></a>例子1</h4><p>这里有一个商品展示的例子,每个商品都有标题,图片,描述和一个购买按钮。设计师的希望每一个商品展示区域都有统一的大小,商品的图片在文字上面,而购买按钮始终和靠在底部,不管这个商品的描述有多长。Flex layout 很容易实现这种设计。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">#detals {</span><br><span class="line">display: flex;</span><br><span class="line">flex-flow: row wrap;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.sale-item {</span><br><span class="line">display: flex;</span><br><span class="line">flex-flow: column;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.sale-item > img {</span><br><span class="line">order: -1;</span><br><span class="line">align-self: center;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.sale-item > button {</span><br><span class="line">margin-top: auto</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><section id="deals"></span><br><span class="line"> <section class="sale-item"></span><br><span class="line"> <h1>Computer Starter Kit</h1></span><br><span class="line"> <p>This is the best computer money can buy, if you don’t have much money.</span><br><span class="line"> <ul></span><br><span class="line"> <li>Computer</span><br><span class="line"> <li>Monitor</span><br><span class="line"> <li>Keyboard</span><br><span class="line"> <li>Mouse</span><br><span class="line"> </ul></span><br><span class="line"> <img src="images/computer.jpg"</span><br><span class="line"> alt="You get: a white computer with matching peripherals."></span><br><span class="line"> <button>BUY NOW</button></span><br><span class="line"> </section></span><br><span class="line"> <section class="sale-item"></span><br><span class="line"> …</span><br><span class="line"> </section></span><br><span class="line"> …</span><br><span class="line"></section></span><br></pre></td></tr></table></figure><h3 id="Module-interactions"><a href="#Module-interactions" class="headerlink" title="Module interactions"></a>Module interactions</h3><p>这个文档扩展了 ‘display’ 的属性,添加了一个新的块级和内联级的 display 的属性,</p>]]></content>
<summary type="html">
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>CSS 2.1 已经定义了四种布局模式:</p>
<blockquote>
<p>block 布局,是为了布局文档设计的(design for laying out document)<br>inline 布局, 是为了布局文本设计的<br>table 布局,以为了以表格的形式展示 2D 数据设计的<br>positioned 布局,是为了显示在文档中完成元素的定位而设计的,这种布局不用顾忌其他元素在文档中的位置。</p>
</blockquote>
<p>这个W3C的文档介绍了一种新的布局模式,flex layout, 这种布局模式是为了更加复杂的web应用和网页设计的。<br>
</summary>
<category term="javascript" scheme="http://blog.boelroy.com/categories/javascript/"/>
</entry>
<entry>
<title>那些前端优化技术背后的原理1</title>
<link href="http://blog.boelroy.com/2016/04/18/javascript/2016-04-18-network-optimise/"/>
<id>http://blog.boelroy.com/2016/04/18/javascript/2016-04-18-network-optimise/</id>
<published>2016-04-17T16:00:00.000Z</published>
<updated>2018-02-23T07:32:56.544Z</updated>
<content type="html"><![CDATA[<h3 id=""><a href="#" class="headerlink" title=""></a><a href="/pics/Speed-up.png"><img src="/pics/Speed-up.png" alt="speed-up"></a></h3><p>现在我们在开发一个网页的时候有很多的优化的最佳实践,诸如将Javascript放在文档的底部。很多时候我们已经将这个最佳实践当成了习惯,其实探究这些方法背后的原理还是挺好玩的一件事。当然在这里Google给出了很详细的文章<a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path/?hl=en" target="_blank" rel="noopener">说明</a></p><p>前端页面的优化其实需要从很多地方来考虑,但是优化的基础就是先弄清楚浏览器是如何展现一个页面的。明白了整个过程,当然知道了性能的瓶颈在哪里,当然也自然知道该如何针对性的去优化。</p><h2 id="Critical-Rendering-Path"><a href="#Critical-Rendering-Path" class="headerlink" title="Critical Rendering Path"></a>Critical Rendering Path</h2><p>当然<code>当你在浏览器中按下回车键到页面展现的整个过程</code>是一个很远古的面试问题,这个 <a href="https://github.com/alex/what-happens-when" target="_blank" rel="noopener">github repo</a> 很清楚而且详细的探究了整个过程。这里重点强调一下整个页面渲染的过程。<br><a id="more"></a></p><h4 id="得到DOM树"><a href="#得到DOM树" class="headerlink" title="得到DOM树"></a>得到DOM树</h4><p>当浏览器获得了请求的HTML之后,会先干一件事情,就是将整个HTML解析成DOM树。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"./test.css"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>Hello <span class="tag"><<span class="name">span</span>></span>web performance<span class="tag"></<span class="name">span</span>></span> students!<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span><span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"awesome-photo.jpg"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>所以上面的HTML会被解析成如下的结构</p><p><a href="/pics/dom-tree.png"><img src="/pics/dom-tree.png" alt="dom-tree"></a></p><p>这里说明一下DOM,当然学JavaScript的人都知道DOM是文档对象模型,他本是是对XML的一个通用的变成接口,但是经过扩展之后就能用于HTML,提供了对HTML删除,添加替换和修改的api。其本身只是一个通用的规范。在这个规范中其实<code>并没有</code>指定文档的结构必须是一个树形的。当然树形结构有他本身的优势,所以基本上所有的说法都会用到 DOM tree 这个词。这里是<a href="https://www.w3.org/TR/DOM-Level-2-Core/introduction.html" target="_blank" rel="noopener">DOM</a>的定义。</p><p>整个解析成DOM树的过程需要消耗一定的时间,尤其是在处理大量的HTML的时候。</p><h4 id="得到CCSOM树"><a href="#得到CCSOM树" class="headerlink" title="得到CCSOM树"></a>得到CCSOM树</h4><p>当浏览器解析了HTML之后,发现了一个stylesheet的标签,所以浏览器立刻发出一个请求,获取test.css的内容</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> { <span class="attribute">font-size</span>: <span class="number">16px</span> }</span><br><span class="line"><span class="selector-tag">p</span> { <span class="attribute">font-weight</span>: bold }</span><br><span class="line"><span class="selector-tag">span</span> { <span class="attribute">color</span>: red }</span><br><span class="line"><span class="selector-tag">p</span> <span class="selector-tag">span</span> { <span class="attribute">display</span>: none }</span><br><span class="line"><span class="selector-tag">img</span> { <span class="attribute">float</span>: right }</span><br></pre></td></tr></table></figure><p>在获取到css之后,和HTML一样,浏览器会做将css解析成一种结构,这里称为CCSOM,CSS对象模型。具体解析如下:</p><p><a href="/pics/cssom-tree.png"><img src="/pics/cssom-tree.png" alt="cssom-tree"></a></p><p>当然这里我们假设一个HTML中没有样式存在,那么是不是可以跳过这个过程呢?答案是否定的,我们都知道浏览器会有默认样式,所以默认样式也会被构建成CCSOM。当然考虑到这些,我们也就知道上图中的树其实是不完整的。</p><h4 id="得到渲染树"><a href="#得到渲染树" class="headerlink" title="得到渲染树"></a>得到渲染树</h4><p>当我们得到了DOM树和CCSOM树之后,我们就能将这两棵树合并成一棵渲染树。这里的渲染树包含了页面上所有的可视元素和这些元素的样式信息。</p><p>这里创建的过程大概如下所示:</p><blockquote><p>1.从DOM树的根节点开始遍历所有的可视节点。</p><p>—-有些不可见的元素(如脚本标签,元数据标签之类的)会被忽略,因为他们不影响页面的渲染结果</p><p>—-有些css隐藏掉的元素也会被忽略。</p><p>2.对于每个可视节点,从CSSOM中寻找对应的样式规则,并付诸节点</p><p>3.输出可视节点,以及每个节点的样式信息</p></blockquote><p>下面就是上述HTML的渲染树:</p><p><a href="/pics/render-tree-construction.png"><img src="/pics/render-tree-construction.png" alt="render-tree-construction"></a></p><h4 id="计算布局和渲染"><a href="#计算布局和渲染" class="headerlink" title="计算布局和渲染"></a>计算布局和渲染</h4><p>在生成了渲染树之后我们就能对页面进行布局了,我们都知道CSS的布局是盒模型,这也是布局阶段最终的输出结果,计算每个元素所占盒子的大小,以及相对于父元素的位置。</p><p>最后我们有了这个盒模型之后就能将每一节点渲染成屏幕上的点。这个过程称为“绘制”。</p><h4 id="页面渲染关键路径"><a href="#页面渲染关键路径" class="headerlink" title="页面渲染关键路径"></a>页面渲染关键路径</h4><p>所以页面渲染的关键路径也就是上述的步骤:</p><blockquote><p>1.生成DOM树</p><p>2.生成CSSOM树</p><p>3.将DOM树和CCSOM树合并成渲染树。</p><p>4.对渲染树进行布局,计算每个节点的几何外观</p><p>5.将渲染树种的每个节点绘制到屏幕上。</p></blockquote><p><a href="/pics/crp.png"><img src="/pics/crp.png" alt="crp"></a></p><h4 id="Javascript-去哪里了?"><a href="#Javascript-去哪里了?" class="headerlink" title="Javascript 去哪里了?"></a>Javascript 去哪里了?</h4><p>这里我们没有讨论到JavaScript对也页面的影响,假设我们有如下的页面</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"./test.css"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span>I love <span class="tag"><<span class="name">script</span>></span><span class="javascript"> <span class="built_in">document</span>.write(<span class="string">'awesome'</span>)</span><span class="tag"></<span class="name">script</span>></span> Javascript <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>我们会在屏幕上看见什么?可能有的人认为是<code>I love Javascript</code>, 但实际上最后我们在屏幕上看见的是<code>I love awesome Javascript</code>。</p><p>为什么会这样?事实上从这个例子我可以看出JavaScript对页面初次渲染产生的影响。</p><p>当DOM树构建到<code>scirpt</code>标签的时候,整个解析会停下来,所以这个时候HTML只解析到<code>script</code>标签之前也就是,然后浏览器会执行脚本,然后将awesome写到了整个文档中,最后才将JavaScript加入到DOM树中去。所以JavaScript脚本会阻塞整个页面的渲染,直到脚本执行完毕。</p><p>这里我们可以得出结论:内联的JavaScript会阻塞DOM树的构建。</p><p>如果我们将Javascript换成外部引用结果会不会不一样呢(如我们用<code><script src="./app.js"></script></code>)。答案是No。整个页面的解析依旧会被阻塞,直到我们从外部加载完app.js,然后执行它之后,才会进行后续的解析。</p><p>当然这里还没有提到css资源和JavaScript的关系。我们知道JavaScript可以修改一个元素的CSS属性,那么这里就有一个trick的地方,当内联的JavaScript执行的时候需要修改样式表但是还没完成CSSOM的加载和创建会怎么样?答案很简单:JavaScript会被延迟加载,直到它完成了CSSOM的下载和构建,当我们在等待的时候DOM构建也被阻塞了。所以其实当我们在构建DOM的时候如果碰到JavaScript,那么<code>DOM构建要同时等待JS的执行和CSS文件的获取</code>。</p><p>下图展示了一个完整的关键渲染路径:</p><h2 id="是时候优化了"><a href="#是时候优化了" class="headerlink" title="是时候优化了"></a>是时候优化了</h2><p>经过上面的分析,critical rendering path(CRP) 决定页面的初次显示。所以优化的重点就是在于尽量减小 CRP 的时间。这里还有各一个概念 critical resouce (关键资源),其实也就是在影响页面初次渲染的资源,如外链的CSS,他会阻塞整个也页面,因为只有加载CSS才能构建CCSOM。这里关键资源包括:HTML,inline CSS, inline JavaSript, extern CSS 和 阻塞的 extern JavaScript(这里说阻塞了表示并非所有JavaScript资源都会阻塞页面的初次渲染)。</p><p>所以这里我们可以从三个方面出发:</p><blockquote><p>1.最小化关键资源的数量</p><p>2.最小化关键资源的字节</p><p>3.最小化关键路径的长度</p></blockquote><h4 id="Minify-Compress-Cache"><a href="#Minify-Compress-Cache" class="headerlink" title="Minify, Compress, Cache"></a>Minify, Compress, Cache</h4><p>对HTML,CSS,JavaScript 文件进行压缩、缓存,可以减小获取文件的传输时间这也是最小化关键资源的字节。</p><h4 id="Inline-CSS"><a href="#Inline-CSS" class="headerlink" title="Inline CSS"></a>Inline CSS</h4><p>这里我们可以将CSS嵌入在HTML中,这样我们可以减少对外部CSS获取时间,这样也能更早的得到页面。但是考虑到实际的生产过程中我们对于一些页面共享的样式的管理,外部CSS还是不可避免的。当然,我们可以将一些关键的样式嵌入在HTML。</p><h4 id="Media-query-on-lt-link"><a href="#Media-query-on-lt-link" class="headerlink" title="Media query on \<link>"></a>Media query on \<link></h4><p>media query 用于对于不同媒体样式的区分,诸如打印,投影之类的(一般不太常见)。但是如果用于响应式网站上,media query 就可以排上大用场。如下面的CSS标签在一般的桌面网站的加载过程中是不会阻塞页面的初次渲染的。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">media</span>=<span class="string">"(max-width: 768px)"</span> <span class="attr">href</span>=<span class="string">"example.css"</span> /></span></span><br></pre></td></tr></table></figure><p>在你访问桌面网站的时候,如果浏览器的宽度大于768px的时候,example.css不会成为关键资源,这样就可以减少关键资源的数量。</p><p>注意这里不会影响关键路径,不表示这个不会被下载和解析。它会以一个较低的优先级进行加载和解析。</p><h4 id="Async-JavaScript"><a href="#Async-JavaScript" class="headerlink" title="Async JavaScript"></a>Async JavaScript</h4><p>上面说过,不管是 inline JavaScript ,还是类似于这种<code><script src="./app.js"></script></code>都会阻塞页面的加载。所以对于JavaScript我们可以用<code>async</code>这个属性将脚本标识为异步的。被标示为<code>async</code>的 script 标签不会阻塞页面的渲染,所以不会加入到关键渲染路径中。他会在脚本文件被下载完成之后被执行,同时会在window的load之前被执行。</p><p>但是async会打乱文件执行的顺序,他并不能保证文件按照他在HTML上出现的顺序被执行。</p><h4 id="动态-Script-tag"><a href="#动态-Script-tag" class="headerlink" title="动态 Script tag"></a>动态 Script tag</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> loadScript = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> script = <span class="built_in">document</span>.createElement(<span class="string">"script"</span>);</span><br><span class="line"> script.type = <span class="string">"text/javascript"</span>;</span><br><span class="line"> script.src = <span class="string">"script1.js"</span>;</span><br><span class="line"> <span class="built_in">document</span>.getElementsByTagName(<span class="string">"head"</span>)[<span class="number">0</span>].appendChild(script);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">document</span>.addEventListener(<span class="string">'DOMContentLoad'</span>, loadScript);</span><br></pre></td></tr></table></figure><p>这里代码很好理解,就是等到DOM构建完成之后我们再去加载这个脚本。这样我们也可以将JavaScript脚本从关键渲染路径当中去除掉</p><h4 id="Ajax-动态获取脚本"><a href="#Ajax-动态获取脚本" class="headerlink" title="Ajax 动态获取脚本"></a>Ajax 动态获取脚本</h4><p>其实和上面代码原理一样,只是获取JS的方式从 Script tag 变成了 通过 XMLHttpRequest 来获取</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loadScript</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line"> xhr.open(<span class="string">"get"</span>, <span class="string">"script1.js"</span>, <span class="literal">true</span>);</span><br><span class="line"> xhr.onreadystatechange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (xhr.readyState == <span class="number">4</span>){</span><br><span class="line"> <span class="keyword">if</span> (xhr.status >= <span class="number">200</span> && xhr.status < <span class="number">300</span> || xhr.status == <span class="number">304</span>){</span><br><span class="line"> <span class="keyword">var</span> script = <span class="built_in">document</span>.createElement (<span class="string">"script"</span>);</span><br><span class="line"> script.type = <span class="string">"text/javascript"</span>;</span><br><span class="line"> script.text = xhr.responseText;</span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(script);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> xhr.send(<span class="literal">null</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>从分析了从获取到HTML之后到整个页面的渲染过程之后,我们可以轻而易举的看出那些优化手段的背后的原理基础。也就跟深刻的理解了页面优化手段。当然这个只是整个网站前端优化的一小部分,我们还要从网络层面,服务器层面去优化。这里先埋个坑,下片文章再补。</p>]]></content>
<summary type="html">
<h3 id=""><a href="#" class="headerlink" title=""></a><a href="/pics/Speed-up.png"><img src="/pics/Speed-up.png" alt="speed-up"></a></h3><p>现在我们在开发一个网页的时候有很多的优化的最佳实践,诸如将Javascript放在文档的底部。很多时候我们已经将这个最佳实践当成了习惯,其实探究这些方法背后的原理还是挺好玩的一件事。当然在这里Google给出了很详细的文章<a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path/?hl=en" target="_blank" rel="noopener">说明</a></p>
<p>前端页面的优化其实需要从很多地方来考虑,但是优化的基础就是先弄清楚浏览器是如何展现一个页面的。明白了整个过程,当然知道了性能的瓶颈在哪里,当然也自然知道该如何针对性的去优化。</p>
<h2 id="Critical-Rendering-Path"><a href="#Critical-Rendering-Path" class="headerlink" title="Critical Rendering Path"></a>Critical Rendering Path</h2><p>当然<code>当你在浏览器中按下回车键到页面展现的整个过程</code>是一个很远古的面试问题,这个 <a href="https://github.com/alex/what-happens-when" target="_blank" rel="noopener">github repo</a> 很清楚而且详细的探究了整个过程。这里重点强调一下整个页面渲染的过程。<br>
</summary>
<category term="javascript" scheme="http://blog.boelroy.com/categories/javascript/"/>
</entry>
<entry>
<title>给我一个承诺,还你一个未来</title>
<link href="http://blog.boelroy.com/2016/03/15/javascript/2015-03-15-promise/"/>
<id>http://blog.boelroy.com/2016/03/15/javascript/2015-03-15-promise/</id>
<published>2016-03-14T16:00:00.000Z</published>
<updated>2018-02-23T08:12:04.285Z</updated>
<content type="html"><![CDATA[<h3 id=""><a href="#" class="headerlink" title=""></a><a href="/pics/promise.png"><img src="/pics/promise.png" alt="qing"></a></h3><p>二次元中有一个词叫做立flag,暗示的剧情的发展。比如如果那个角色说了一句“我保证会活着回来”,基本上这个角色就离死不远了。Javascript 最近几年也发展出类似的玩意儿,表示将来的某种状态,叫做 Promises (当然这里没有死这么严重),早期 jQuery 的 Deferred 就是类似于 Promises 的实现(当然jQuery的Deferred不完全符合Promises现在的标准)。</p><h2 id="聊聊-Promises-的历史"><a href="#聊聊-Promises-的历史" class="headerlink" title="聊聊 Promises 的历史"></a>聊聊 Promises 的历史</h2><p>所以,为什么Promises会出现?Promises最原始的是由 Daniel P. Friedman 和 David Wise 提出的。后来有出现了类似的概念叫做Futures。Futures和Promises的出现是为了解决并行编程中同步的问题。有关他们的介绍可以看这里 <a href="https://en.wikipedia.org/wiki/Futures_and_promises" target="_blank" rel="noopener">Futures and Promises</a>。</p><p>虽然是搬运 wikipedia 的,还是要说一下一般我们会看到几个词 future, promise, deferred, delay 一般来说这几个词可以等价。但是按照原始的定义,或者更确切的从原始的意义上来解释 Futures 和 Promises 还是有细微的不同的。Futures 指的一个只读的变量的占位符,意思就是说 Futures 作为一个异步操作的符号表示,表示这个地方会有一个异步操作的返回。而 Promises 指一个可以对 Futures 进行设置或者操作的容器。 这在单词的字面的意思也能理解,未来是一种代指之后的某一时刻,而承诺本身就隐性地包含了未来。<br><a id="more"></a></p><h2 id="异步编程与回调"><a href="#异步编程与回调" class="headerlink" title="异步编程与回调"></a>异步编程与回调</h2><p>Futures 和 Promises 的提出就是用来解决异步编程的,所以先来看看javascript是如何解决异步编程问题的。</p><p>异步编程是我们执行一个函数的时候,可能本身这个函数的执行是费时间的,可是我们不希望这个操作的执行阻塞了当前的线程,希望这个函数能够立即返回,让这个操作在不影响当前线程的情况下运行,然后在将来的某一时刻,操作完成之后通知当前线程,更新状态。</p><p>一般来说,javascript 的异步编程基本上依靠的回调函数,不管是最原始的回调,还是 event-drive的方式,或者基于pub-sub的方式,还是我们现在正在讨论的 Promises,本质上都是依靠了回调函数。只是在方式上简化了原始回调的操作,努力去避免了一些在代码组织上的问题。</p><h4 id="原始回调"><a href="#原始回调" class="headerlink" title="原始回调"></a>原始回调</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">op1(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> op2(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> op3(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">op1</span>(<span class="params">callback</span>) </span>{</span><br><span class="line"> <span class="comment">// some async things</span></span><br><span class="line"> <span class="comment">// when finish execute callback();</span></span><br><span class="line"> reuturn; <span class="comment">// immediately return</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// more simple way</span></span><br><span class="line"></span><br><span class="line">op1(cb1)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">cb1</span>(<span class="params"></span>)</span>{</span><br><span class="line"> op2(cb2)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">cb2</span>(<span class="params"></span>)</span>{</span><br><span class="line"> op3();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这是最原始的方法,把函数当作参数传递给异步操作,等到异步操作完成之后,调用回调函数。典型的例子就是setTimeout。</p><p>但是原始的回调函数有一个缺点是,当我们的程序越来越复杂的时候,我们的回调的层数会越来越多,代码的耦合性高,在代码的可维护性上就出现问题。想象一下如果我们有5个ajax请求顺序执行,这样就有五层回调,这样如果当我们突然说不行 我们要去掉中间的两个回调,这样带来的代码量的修改是非常大的。</p><h4 id="事件驱动"><a href="#事件驱动" class="headerlink" title="事件驱动"></a>事件驱动</h4><p>事件驱动可以很好的解决掉这个原始回调带来的问题,考虑如下代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async1</span>(<span class="params"></span>)</span>{</span><br><span class="line"> async1.trigger(<span class="string">'async1-done'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async2</span>(<span class="params"></span>)</span>{ </span><br><span class="line"> async2.trigger(<span class="string">'async2-done'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async3</span>(<span class="params"></span>)</span>{</span><br><span class="line"> async3.trigger(<span class="string">'async3-done'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">async1.on(<span class="string">'async1-done'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> async2()</span><br><span class="line">})</span><br><span class="line">async2.on(<span class="string">'async2-done'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> async3()</span><br><span class="line">})</span><br><span class="line">async3.on(<span class="string">'async3-done'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="comment">// some thing</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">async1();</span><br></pre></td></tr></table></figure><p>我们看到这里的代码的耦合程度,从代码的可维护性来说,显然这里的事件驱动会明显好于前一种。</p><h3 id="pub-sub-发布订阅"><a href="#pub-sub-发布订阅" class="headerlink" title="pub-sub(发布订阅)"></a>pub-sub(发布订阅)</h3><p>发布订阅是在事件驱动的基础上,把能触发事件和发布事件统一在一起,便于对事件的管理,这样避免在纯事件驱动中的事件种类不可控性。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async1</span>(<span class="params"></span>)</span>{</span><br><span class="line"> EventBus.trigger(<span class="string">'async1-done'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async2</span>(<span class="params"></span>)</span>{ </span><br><span class="line"> EventBus.trigger(<span class="string">'async2-done'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async3</span>(<span class="params"></span>)</span>{</span><br><span class="line"> EventBus.trigger(<span class="string">'async3-done'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">EventBus.on(<span class="string">'async1-done'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> async2()</span><br><span class="line">})</span><br><span class="line">EventBus.on(<span class="string">'async2-done'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> async3()</span><br><span class="line">})</span><br><span class="line">EventBus.on(<span class="string">'async3-done'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="comment">// some thing</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">async1();</span><br></pre></td></tr></table></figure><p>这里我们还能对Eventbus限制,比如只能触发规定事件,或者只能监听规定事件,这样对面对逐渐扩大的项目,不会出现事件混乱的情况出现。</p><h2 id="Promises-解决方案"><a href="#Promises-解决方案" class="headerlink" title="Promises 解决方案"></a>Promises 解决方案</h2><p>其实不能说Promises更优于上面所说的事件驱动的异步方案或者基于订阅发布的方案。但是Promises带来的更优雅的方式。</p><p>Promises 在2007年第一次被Dojo所实现,称为dojo.Deferred。之后CommonJS一直致力于标准化Promises行为,现在最被广泛接受的是<a href="https://promisesaplus.com/" target="_blank" rel="noopener">Promises/A+</a>。所以这里以Promises/A+为例说明Promises。上面的代码如过用Promises可以表述成:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async1</span>(<span class="params">resolve, reject</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="comment">//sucess condition) {</span></span><br><span class="line"> resolve(<span class="comment">//async1result)</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> reject(<span class="comment">//reason)</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async2</span>(<span class="params">resolve, reject</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="comment">//sucess condition) {</span></span><br><span class="line"> resolve(<span class="comment">//async2result)</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> reject(<span class="comment">//reason)</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">async3</span>(<span class="params">resolve, reject</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="comment">//sucess condition) {</span></span><br><span class="line"> resolve(<span class="comment">//someresult)</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> reject(<span class="comment">//reason)</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(aysnc1)</span><br><span class="line"> <span class="comment">// basic uses</span></span><br><span class="line"> .then(<span class="function"><span class="keyword">function</span>(<span class="params">result</span>)</span>{</span><br><span class="line"> <span class="comment">// handler sucess</span></span><br><span class="line"> <span class="keyword">return</span> callback_result1</span><br><span class="line"> }, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>)</span>{</span><br><span class="line"> <span class="comment">// error handler</span></span><br><span class="line"> })</span><br><span class="line"> <span class="comment">// chain on same result</span></span><br><span class="line"> .then(<span class="function"><span class="keyword">function</span>(<span class="params">callback_result1</span>)</span>{</span><br><span class="line"> <span class="comment">// handler sucess</span></span><br><span class="line"> <span class="keyword">return</span> callback_result2</span><br><span class="line"> }, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>)</span>{</span><br><span class="line"> <span class="comment">// error handler</span></span><br><span class="line"> })</span><br><span class="line"> <span class="comment">// chain on different promise</span></span><br><span class="line"> .then(<span class="function"><span class="keyword">function</span>(<span class="params">result</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(async2)</span><br><span class="line"> }, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>)</span>{</span><br><span class="line"> <span class="comment">// error handler</span></span><br><span class="line"> })</span><br><span class="line"> .then(<span class="function"><span class="keyword">function</span>(<span class="params">async2_result</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(async3)</span><br><span class="line"> }, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>)</span>{</span><br><span class="line"> <span class="comment">// error handler</span></span><br><span class="line"> })</span><br><span class="line"> .then(<span class="function"><span class="keyword">function</span>(<span class="params">async3_result</span>)</span>{</span><br><span class="line"> <span class="comment">// some thing</span></span><br><span class="line"> }, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>)</span>{</span><br><span class="line"> <span class="comment">// error handler</span></span><br><span class="line"> })</span><br></pre></td></tr></table></figure><p>这里先说明一下, 在Promise中一共只有三个状态pending, fullfilled, rejected. pending 表示异步操作还在进行,fullfilled表示这个异步操作已经成功,rejected表示这个异步操作失败了。</p><p>我们在实现async1时我们传入了两个参数resolve和reject,他们都是函数。resolve表示这个操作成功,他接受一个result作为参数。reject表示这个操作失败,接受一个reason参数。然后在这个Promise的then方法我们会传入两个函数,第一个会在resolve调用的时候触发,表示操作成功之后的回调。第二个会在reject调用的时候触发表示这个操作失败之后处理错误。介绍到这里就算是promise的基本用法了。</p><p>但是并不是Promise强大的地方。</p><h3 id="同一异步操作的链式调用"><a href="#同一异步操作的链式调用" class="headerlink" title="同一异步操作的链式调用"></a>同一异步操作的链式调用</h3><p>promise允许在同一个异步调用上反复的使用then,考虑上面代码第二个then,即使在对于async1这个操作的结果进行链式的调用。第一个then中成功回调接受到async1的返回之后处理,然后再进入到第二个then中的成功回调中。这个第二个回调中的参数就是第一个回调的返回。这样我们可以一步一步的对异步操作的原始结果做链式调用,一步一步的处理数据。大大清晰了整个过程。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(asyncOperation).then(processData1).then(processData2).then(processData3)</span><br></pre></td></tr></table></figure><p>注意这里,每次then对象的调用实际上是生成的一个新的Promise对象,并不能将这里的链式调用等同于jQuery中的链式调用,应为jQuery的链式调用每次都返回的是同一个对象。</p><h3 id="不同异步操作的链式调用"><a href="#不同异步操作的链式调用" class="headerlink" title="不同异步操作的链式调用"></a>不同异步操作的链式调用</h3><p>上面的代码的第三个then显示了对于多个异步流程的处理。现在我们的流程是async1->async2->async3, 在pub-sub那节中已经给出了传统的实现。然后考虑上面Promise的处理。Promise前两个then是对于第一个回调的处理,这个已经提到了。第三个then,我们可以看到这个地方在最后返回了一个async2的Promise对象。然后在第四个then中的success回调中我们就能得到async2的结果,然后再返回async3的Promise对象。这样我们就实现了async1->async2->async3的异步流程的链式操作。如果中间比如async2我们不需要了,则只需要将第三个then删除,其他的地方完全不用修改代码。这样的写法必然要比用pub-sub或者event-drive的方式要简单明了。也一眼能看出数据流的方向。</p><h3 id="错误异常处理"><a href="#错误异常处理" class="headerlink" title="错误异常处理"></a>错误异常处理</h3><p>你可以直接在异步操作的过程中或者在对结果的处理过程中,抛出任何的错误。Promise都会帮你传递到下一个then的错误处理中。对异常和错误的处理十分的方便</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>)</span>{</span><br><span class="line"> <span class="keyword">throw</span> e</span><br><span class="line">}).then(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"></span><br><span class="line">}, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span><br><span class="line"></span><br><span class="line">})</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>)</span>{</span><br><span class="line"> resolve(e)</span><br><span class="line">}).then(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"This is an error"</span>)</span><br><span class="line">}, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"></span><br><span class="line">}).then(<span class="literal">null</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(e)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>注意,这里的错误处理不会一直传递下去,只会错误出现的下一个then中的错误的处理中被接收到。再下一个then的那个回调会被触发完全取决于你的返回。</p><h3 id="回调如何触发"><a href="#回调如何触发" class="headerlink" title="回调如何触发"></a>回调如何触发</h3><p>在异步函数中resolve触发success,reject触发fail。但是如果有多个then,那么then的触发取决于上一个回调的返回。</p><p>如果是返回值不是没有then方法的函数或者对象,就会触发success的回调,参数就是这个返回值</p><p>如果是throw exception 则会触发fail的回调。</p><p>如果返回是个Promise对象则取决于Promise的自身的状态,Promise是fullfill的则触发succes,如果是rejected,则触发fail的回调。然后他们的参数就是这个Promise的value或者reason。</p><p>如果返回值是有then方法的函数或者对象,这个就取决于then中的处理。这里then方法有两个参数 resolvePromise和rejectPromise,如果执行resovlePromise(x),则下一个then会触发success,如果是执行rejectPromise(reason)则会触发下一个then的fail回调。</p><h2 id="自己实现一个Promise"><a href="#自己实现一个Promise" class="headerlink" title="自己实现一个Promise"></a>自己实现一个Promise</h2><p>这里限于篇幅我可能会在之后加入如果根据Promise/A+的规范实现一个Promise,详情可以参考我的这个简单实现<a href="https://github.com/Boelroy/Future.js" target="_blank" rel="noopener">future.js</a></p>]]></content>
<summary type="html">
<h3 id=""><a href="#" class="headerlink" title=""></a><a href="/pics/promise.png"><img src="/pics/promise.png" alt="qing"></a></h3><p>二次元中有一个词叫做立flag,暗示的剧情的发展。比如如果那个角色说了一句“我保证会活着回来”,基本上这个角色就离死不远了。Javascript 最近几年也发展出类似的玩意儿,表示将来的某种状态,叫做 Promises (当然这里没有死这么严重),早期 jQuery 的 Deferred 就是类似于 Promises 的实现(当然jQuery的Deferred不完全符合Promises现在的标准)。</p>
<h2 id="聊聊-Promises-的历史"><a href="#聊聊-Promises-的历史" class="headerlink" title="聊聊 Promises 的历史"></a>聊聊 Promises 的历史</h2><p>所以,为什么Promises会出现?Promises最原始的是由 Daniel P. Friedman 和 David Wise 提出的。后来有出现了类似的概念叫做Futures。Futures和Promises的出现是为了解决并行编程中同步的问题。有关他们的介绍可以看这里 <a href="https://en.wikipedia.org/wiki/Futures_and_promises" target="_blank" rel="noopener">Futures and Promises</a>。</p>
<p>虽然是搬运 wikipedia 的,还是要说一下一般我们会看到几个词 future, promise, deferred, delay 一般来说这几个词可以等价。但是按照原始的定义,或者更确切的从原始的意义上来解释 Futures 和 Promises 还是有细微的不同的。Futures 指的一个只读的变量的占位符,意思就是说 Futures 作为一个异步操作的符号表示,表示这个地方会有一个异步操作的返回。而 Promises 指一个可以对 Futures 进行设置或者操作的容器。 这在单词的字面的意思也能理解,未来是一种代指之后的某一时刻,而承诺本身就隐性地包含了未来。<br>
</summary>
<category term="javascript" scheme="http://blog.boelroy.com/categories/javascript/"/>
</entry>
<entry>
<title>我的2015</title>
<link href="http://blog.boelroy.com/2016/03/10/life/plan/"/>
<id>http://blog.boelroy.com/2016/03/10/life/plan/</id>
<published>2016-03-09T16:00:00.000Z</published>
<updated>2018-02-23T07:32:56.543Z</updated>
<content type="html"><![CDATA[<h3 id=""><a href="#" class="headerlink" title=""></a><a href="/assets/pics/2016plan.jpeg"><img src="/pics/2016plan.jpeg" alt="qing"></a></h3><p>对于现在已经过去了四分之一的2016年来说,现在才开始写2015年的总结似乎有点嘲讽。啊啊啊啊啊啊!可恶的拖延症( ゜- ゜)</p><h2 id="幸运的2015"><a href="#幸运的2015" class="headerlink" title="幸运的2015"></a>幸运的2015</h2><p>记得18岁时候进大学的时候,少不更事,满是铿锵抱负,但是到最后只是平淡失意的过完了四年。过去五年中的前面四年,在印象中没有交到过什么好运。至少心里想做成的事,似乎也没完成啥。所以2015年,至少和前四年比起来是有那么一点小幸运。</p><p>这一年总结下来也就几件事,考上研,完成了生涯中最大一个项目,开始经济自给,体验了完全不同的大学生活。<br><a id="more"></a></p><h4 id="考研"><a href="#考研" class="headerlink" title="考研"></a>考研</h4><p>考研其实并不是什么可以说道的事,没去成BAT之后,只能很失败的选择考研,然后保险的选择考本校。本来想想这应该是一件不是很困难的事情。直到发着烧,拉着肚子进了考场之后才发现生活又开启了hard模式。在成绩出来之前,诚惶诚恐的过完年。幸运的是成绩还算不错,也算是前几年积攒下来的人品。</p><h4 id="项目"><a href="#项目" class="headerlink" title="项目"></a>项目</h4><p>其实说到这个今年算是经历丰富,经历了<a href="http://www.bugtags.com/" target="_blank" rel="noopener">Bugtags</a>开始创业到项目完成到现在做得好不错。特别感谢<a href="http://www.bugtags.com/" target="_blank" rel="noopener">bugtags</a>的成员的信任,让我独立的完成了前端页面的架构。真的是我最近几年内完成最大的一个项目了。</p><h4 id="经济自给"><a href="#经济自给" class="headerlink" title="经济自给"></a>经济自给</h4><p>本来想说经济独立,后来想想还是说经济自给更合适一点。没有什么能比作为一个学生自己能养活自己更开心的事情了。所以可能这一年最开心的事情就是,自己交自己的学费,给自己换了电脑、手机,吃好吃的。当然这很大程度的牺牲掉了一些自由时间,也养成了3点之后才能睡觉的习惯。</p><h4 id="大学生活"><a href="#大学生活" class="headerlink" title="大学生活"></a>大学生活</h4><p>说到大学生活,给我的感觉是之前四年的大学完全是白上了。那种不安,局促,不知道未来在那里的状态和现在完全是两种不同的人生体验。虽然现在也不安着,这种不安反倒是成了自己使劲push自己推力。这种状态应该算是来自于自己的想法和能力的矛盾,总想做好每一件事,反倒发现最后都是自己自不量力的作死( ̄◇ ̄;),不过幸好还算是乐在其中。</p><h2 id="新年计划"><a href="#新年计划" class="headerlink" title="新年计划"></a>新年计划</h2><p>订计划这种活总是吃力不讨好的事情,每次订完的计划,似乎也没按计划完成过。不过人总是要有个盼头,计划这种东西还是要有一个。</p><p>所以2016年希望能多攒钱(因为去年赚的钱全花掉了),去BAT一家实习(杰伦导师,MGF这三家能算是梦想么),每月花100去买书然后读完(这个好像很困难),继续提升前端技术。当然最重要的是,我要出去玩,澳门和杭州都在计划之内了,你们要等我过去啊!!!!</p>]]></content>
<summary type="html">
<h3 id=""><a href="#" class="headerlink" title=""></a><a href="/assets/pics/2016plan.jpeg"><img src="/pics/2016plan.jpeg" alt="qing"></a></h3><p>对于现在已经过去了四分之一的2016年来说,现在才开始写2015年的总结似乎有点嘲讽。啊啊啊啊啊啊!可恶的拖延症( ゜- ゜)</p>
<h2 id="幸运的2015"><a href="#幸运的2015" class="headerlink" title="幸运的2015"></a>幸运的2015</h2><p>记得18岁时候进大学的时候,少不更事,满是铿锵抱负,但是到最后只是平淡失意的过完了四年。过去五年中的前面四年,在印象中没有交到过什么好运。至少心里想做成的事,似乎也没完成啥。所以2015年,至少和前四年比起来是有那么一点小幸运。</p>
<p>这一年总结下来也就几件事,考上研,完成了生涯中最大一个项目,开始经济自给,体验了完全不同的大学生活。<br>
</summary>
<category term="碎碎念" scheme="http://blog.boelroy.com/categories/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
</entry>
<entry>
<title>若没有离别,成长也无所附丽</title>
<link href="http://blog.boelroy.com/2014/07/14/life/2014-07-14-graduaction/"/>
<id>http://blog.boelroy.com/2014/07/14/life/2014-07-14-graduaction/</id>
<published>2014-07-13T20:07:41.000Z</published>
<updated>2018-02-23T07:32:56.543Z</updated>
<content type="html"><![CDATA[<p>再过几个小时就是世界杯决赛,于是在这空白的几个小时中,我决定一边吹着冷气一边码字。其实我也不知道为什么会决定码字来打发时光,因为我很清晰地记得距我上次码字已经过去了两年,估计就是心血来潮了吧。<br><a id="more"></a><br>其实在每一段赤诚的叙述或着回忆的开始之前,都是困顿。</p><p><a href="/pics/graduate.jpg"><img src="/pics/graduate.jpg" alt="qing"></a></p><p>嗯……如果我的青春用这样的方式来说——</p><blockquote><p>四年前,在入学考试的四教,一个面向很成熟的学生问大汉,你知道421在哪么?之后我们六个人终于认识了。直到后来我们还不时的开东来玩笑,当时真的以为你是老师呢。 </p></blockquote><blockquote><p>三年前的六月,当时在大学过得不是很愉快的我,每天没日没夜的在网吧打游戏。其实我也不知道为什么过得不愉快,可能是对大学失望了吧。不过万幸,和室友一起刷夜,没有挂科,这也是我很一直很“遗憾“的一件事。 </p></blockquote><blockquote><p>一年前的实习,东来找了女朋友,小松和女朋友分手了。似乎很多事情在这个夏天集中爆发。我记得很长的一段时间我都在写码,着急自己的人生,着急未来。不过很庆幸当时我认识了很多人经历了很多事。 </p></blockquote><blockquote><p>一个星期以前,我看着那些四年的面孔和我干杯、拥抱,一个一个哭得和泪人一样,突然觉得自己已经毕业了。而之后,我亲手把他们一个一个送离天津。 </p></blockquote><blockquote><p>几个小时之前,在微信群中大家还在为”葱猫“和”帆卷“的电视剧编写着剧本。 </p></blockquote><p>用这样一串仓促的排比句来整理时光的脉络,突然有一种一页页翻书的感觉。似乎曾经所有的开心喜悦和辛酸苦痛通通都在里面。其实最近的很长时间,这些回忆就在我的脑海里翻滚。每次心绪都会潮伏壮阔,久久不宁。我记得在威海时大家明媚的笑脸,还记得杀人游戏大家紧闭的双眼,还记得散伙饭时哽咽失声,语焉不详的话语。想到这些,突然觉得四年的距离其实并没有那遥远。</p><p>以前有时候总是有意无意的说——时间还早呢——好像青春还很长很长,所以即使坐着世界上最无趣最枯燥的事情,都不会觉得活的暗淡。但是时间其实就是一个小偷,它偷走了我们的青春,偷走了我们四年的年华。我们之间的故事似乎没有拉开帷幕,就要散场了。</p><p>于是在所剩不多的日子,我们以一种近乎抗拒的姿态,肆意玩耍,肆意疯狂。这的确是一件矫情的事,我们兴师动众地试图抗拒时光的力量,要将所有日后可能会变得语焉不详的记忆一丝不苟的镌刻在一张张胶质画片儿上。但是,我在听到《海阔天空》的时候能够哭出来,事后狠狠高兴的骂了自己一句:原来自己真他么的矫情!</p><p>我记得在摆完摊后吃饭的那个晚上,我们一起去了KTV。我们一起唱《再见》,《凤凰花开的路口》。我唱着唱着觉得内心突然空旷了起来,耳边巨大嘈杂的声音突然渐渐安静。透过微弱的灯光,你们棱角分明的脸特别清晰的展现在我眼前。那是我真地觉得怕突然的某一天看到你们的脸而记不起你们的名字而努力的去记住每一张脸。</p><p>有时候我会觉得生命只是一把尺子,如果嫌太长你可以中途折断;但是如果嫌太短,却无论如何都拉不长。一帆说过一句话,大概意思是它痛恨我们班最后的时刻才留下这么多美好的回忆,而不能把回忆的时间变得更长。青春只不过是在生命这样一把尺子占据一段长度,而那些美好的回忆和这一段长度相比似乎显得更为短暂。</p><p>我特别喜欢的一句台词:Today is the first day of the rest of your life. This is the right with every day except one day : the day you die。既然如此,在我们活过来的生命以及将要来临的生命中,我会努力将这些回忆变成自己努力活得丰盛的动力。</p><p>其实,毕业最后对我来说就是告别。送走每个同学的时候,我坚持为他拎着行李。我不是觉得之后再也见不到他们了,我只是觉得这是我必须要做的事罢了。本来没打算要哭,可是送走东来的时候还是哭了出来。只是因为再也不能一起生活了。以前一直觉得自己是一个恪守冷暖自知的人,到最后发现其实也不过如此。</p><p>现在每天7点半起,骑车去学校,做高数,做阅读,背单词。似乎要我一口气补上我大学四年中逃掉的所有课。每天很忙,有时候会觉得自己轻易的就从毕业的氛围中抽身出来,内心袭来一股内疚感,然后会想起左小祖咒的歌,</p><blockquote><p>当我推开那扇门<br>想看看永恒荣光的状景<br>那没有他们说的实用阶梯然而我<br>又不能悲伤地坐在你身旁<br>我不能悲伤地坐在你身旁<br>在我走出那扇门<br>撕下某本书的二百五十二页<br>它用黑色镶金这般地写着:<br>Hey我不能悲伤地坐在你身旁<br>我不能悲伤地坐在你身旁<br>我不能悲伤地坐在你身旁<br>…… </p></blockquote><p>我知道我可能不能开心的坐在你们身旁了</p><p>可是,若没有离别,成长也无所附丽。</p>]]></content>
<summary type="html">
<p>再过几个小时就是世界杯决赛,于是在这空白的几个小时中,我决定一边吹着冷气一边码字。其实我也不知道为什么会决定码字来打发时光,因为我很清晰地记得距我上次码字已经过去了两年,估计就是心血来潮了吧。<br>
</summary>
<category term="碎碎念" scheme="http://blog.boelroy.com/categories/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
</entry>
</feed>