-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
2158 lines (1522 loc) · 157 KB
/
index.html
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Why Y</title>
<meta name="author" content="">
<meta name="description" content="最近要在 Redis 里放一点敏感数据,于是就在 Rails 里找实现加密的东西。Rails 有 cookies.encrypted 和 cookies.signed 两个方法,区别一直没搞明白,这次研究了一下,终于明白了。 加密 加密的目的是保证数据只能通过秘钥才能查看,没有秘钥看不到任何数据 …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://teeceepee.github.com">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="Why Y" type="application/atom+xml">
<script src="/javascripts/modernizr-2.0.js"></script>
<!--
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="./javascripts/lib/jquery.min.js"%3E%3C/script%3E'))</script>
-->
<script src="/javascripts/libs/jquery.min.js" type="text/javascript"></script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-67854007-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">Why Y</a></h1>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="http://google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="q" value="site:teeceepee.github.com" />
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2017/02/14/difference-between-encrypting-and-signing/">加密与签名的区别</a></h1>
<p class="meta">
<time datetime="2017-02-14T22:48:00+08:00" pubdate data-updated="true">Feb 14<span>th</span>, 2017</time>
</p>
</header>
<div class="entry-content"><p>最近要在 Redis 里放一点敏感数据,于是就在 Rails 里找实现加密的东西。Rails 有 <code>cookies.encrypted</code> 和 <code>cookies.signed</code> 两个方法,区别一直没搞明白,这次研究了一下,终于明白了。</p>
<h2>加密</h2>
<p>加密的目的是保证数据只能通过秘钥才能查看,没有秘钥看不到任何数据。</p>
<h2>签名</h2>
<p>签名的目的是保证数据不会被篡改或伪造,但是可以在没有秘钥的情况下查看原数据!</p>
<h2>示例</h2>
<p>ActiveSupport 中有加密和签名的功能,对应的类是 ActiveSupport::MessageEncryptor 和 ActiveSupport::MessageVerifier。下面的例子展示了它们的用法以及区别。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">secret</span> <span class="o">=</span> <span class="s1">'secret'</span> <span class="o">*</span> <span class="mi">10</span>
</span><span class='line'><span class="n">encryptor</span> <span class="o">=</span> <span class="ss">ActiveSupport</span><span class="p">:</span><span class="ss">:MessageEncryptor</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">secret</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">encrypted_data</span> <span class="o">=</span> <span class="n">encryptor</span><span class="o">.</span><span class="n">encrypt_and_sign</span><span class="p">(</span><span class="s1">'You cannot see me'</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># with secret</span>
</span><span class='line'><span class="n">encryptor</span><span class="o">.</span><span class="n">decrypt_and_verify</span><span class="p">(</span><span class="n">encrypted_data</span><span class="p">)</span>
</span><span class='line'><span class="c1"># => "You cannot see me"</span>
</span><span class='line'><span class="c1"># You can do nothing without secret</span>
</span></code></pre></td></tr></table></div></figure>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">secret</span> <span class="o">=</span> <span class="s1">'secret'</span> <span class="o">*</span> <span class="mi">10</span>
</span><span class='line'><span class="n">verifier</span> <span class="o">=</span> <span class="ss">ActiveSupport</span><span class="p">:</span><span class="ss">:MessageVerifier</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">secret</span><span class="p">)</span>
</span><span class='line'><span class="n">signed_data</span> <span class="o">=</span> <span class="n">verifier</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="s1">'You cannot see me'</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># with secret</span>
</span><span class='line'><span class="n">verifier</span><span class="o">.</span><span class="n">verify</span><span class="p">(</span><span class="n">signed_data</span><span class="p">)</span>
</span><span class='line'><span class="c1"># => "You cannot see me"</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># without secret !!!</span>
</span><span class='line'><span class="n">encoded</span> <span class="o">=</span> <span class="n">signed_data</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'--'</span><span class="p">)</span><span class="o">.</span><span class="n">first</span>
</span><span class='line'><span class="no">Marshal</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="no">Base64</span><span class="o">.</span><span class="n">strict_decode64</span><span class="p">(</span><span class="n">encoded</span><span class="p">))</span>
</span><span class='line'><span class="c1"># => "You cannot see me"</span>
</span></code></pre></td></tr></table></div></figure>
<p>值得一提的是,ActiveSupport::MessageEncryptor 除了加密,也提供了签名的功能,从方法名中可以看出这一点。</p>
<h2>参考</h2>
<p><a href="http://www.monkeyandcrow.com/blog/reading_rails_how_does_message_verifier_work/">Reading Rails - How Does MessageVerifier Work?</a></p>
<p><a href="http://www.monkeyandcrow.com/blog/reading_rails_how_does_message_encryptor_work/">Reading Rails - How Does MessageEncryptor Work?</a></p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2016/11/27/jquery-utils-replacement/">jQuery Utils Replacement</a></h1>
<p class="meta">
<time datetime="2016-11-27T16:25:00+08:00" pubdate data-updated="true">Nov 27<span>th</span>, 2016</time>
</p>
</header>
<div class="entry-content"><p>曾几何时,编写跨浏览器的 JavaScript 代码是噩梦一般的存在。在那个浏览器差异巨大、标准提供的功能有限的时代,jQuery 出现后,依靠跨浏览器、功能丰富、接口简洁优雅的特性,迅速成为了前端开发的事实标准。直到现在,jQuery 可能依然是最流行的 JavaScript 库。但是到了现在,现代浏览器不再各自为战,开始遵循相同的标准,而标准本身也在前进,不断加入新的接口。很多逻辑已经可以使用原生的接口实现,不再依赖第三方库。</p>
<p>在我看来,jQuery 的 DOM 操作接口依旧比原生接口好用,还是很难丢弃掉。但很多实用函数已经可以使用原生接口替代。下面整理了几个 jQuery 常用的实用函数,以及对应的原生接口。</p>
<h2>$.each() 和 $.fn.each()</h2>
<p>分别使用 <code>Array.prototype.forEach()</code> 和 <code>NodeList.forEach()</code> 替代。</p>
<p>曾经 JavaScript 只能不停地使用循环来实现遍历,ECMAScript 5 新增了数组等类型的 <code>forEach</code> 方法,因此已经可以不使用 jQuery 的遍历函数了。而且现在最新版本的浏览器也开始支持 NodeList 实例的 <code>forEach</code> 方法。需要注意的是参数顺序是不一样的,<code>forEach</code> 的参数顺序更方便一些,因为很多时候并不需要下标。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">array</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">array</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">item</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">$</span><span class="p">(</span><span class="nx">selector</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">el</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="nx">selector</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">item</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach</a></p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach">https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach</a></p>
<h2>$.extend()</h2>
<p>使用 <code>Object.assign()</code> 替代。</p>
<p>很多库和框架都有自己的对象合并方法,方法名称一般是 <code>extend</code> 。ECMAScript 2015 新增了原生的对象合并方法,只不过名字叫 <code>assgin</code>。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">extend</span><span class="p">({},</span> <span class="nx">objA</span><span class="p">,</span> <span class="nx">objB</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span> <span class="nx">objA</span><span class="p">,</span> <span class="nx">objB</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign</a></p>
<p><a href="https://github.com/nzakas/understandinges6/issues/99">https://github.com/nzakas/understandinges6/issues/99</a></p>
<h2>$.map()</h2>
<p>使用 <code>Array.prototype.map()</code> 替代。</p>
<p>同样需要注意参数顺序。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">array</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">array</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map</a></p>
<h2>$.proxy()</h2>
<p>使用 <code>Function.prototype.bind()</code> 替代。</p>
<p>函数的 <code>bind</code> 方法是 EMCAScript 5 中新增的非常重要的一个方法,有了它可以尽量避免 <code>that</code>, <code>self</code> 之类的愚蠢变量名。需要注意的是 <code>bind</code> 每次调用都返回一个新的函数,而 <code>$.proxy</code> 在内部做了缓存处理,多次调用返回的是同一个函数。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">proxy</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="nx">context</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nx">fn</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">context</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind</a></p>
<p><a href="http://stackoverflow.com/questions/18848343/underscore-bind-vs-jquery-proxy-vs-native-bind#answer-22860661">http://stackoverflow.com/questions/18848343/underscore-bind-vs-jquery-proxy-vs-native-bind#answer-22860661</a></p>
<h2>$.trim()</h2>
<p>使用 <code>String.prototype.trim()</code> 替代。</p>
<p>MDN 中给出了一个 polyfill 的方法,jQuery 的实现几乎与其一样,<code>$.trim()</code> 不是字符串类型的方法,所以对 <code>null</code> 做了特殊处理,<code>$.trim(null) === ‘’</code>。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">trim</span><span class="p">(</span><span class="nx">string</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nx">string</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim</a></p>
<h2>总结</h2>
<p>本文是阅读 <a href="http://youmightnotneedjquery.com/">You Might Not Need jQuery</a> 后写的,页面上有一个选择支持 IE 最老版本的按钮,通过切换不同的开关,可以看到越新的浏览器版本,实现相同功能的代码越简洁统一。相信在浏览器厂商和标准的共同努力下,可以让我们更好得使用原生接口编写前端代码。</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2016/07/10/rails-time-zone/">Rails Time Zone</a></h1>
<p class="meta">
<time datetime="2016-07-10T14:22:00+08:00" pubdate data-updated="true">Jul 10<span>th</span>, 2016</time>
</p>
</header>
<div class="entry-content"><p>前几天发现自己写的 Rails 项目数据库里的时间不对,与实际时间相差了八个小时。作为一个生活在东八区的人,很容易理解这是一个与时区相关的问题。</p>
<p>首先检查 Rails 的配置,发现没有与时区相关的配置,说明这是 Rails 的默认行为。查了一些资料后,慢慢理解了 Rails 是如何处理时区的。</p>
<p>首先是如何向数据库中插入时间。通过数据库迁移生成的时间和日期类型的字段是没有时区信息的。<code>created_at</code> 在 PostgreSQL 中是 <code>timestamp without time zone</code> 类型的,在 MySQL 中是 <code>datetime</code> 类型的,显然都是不带时区信息的。Rails 默认在数据库中保存 UTC 时间,也可以通过修改配置让数据库中保存的是本地时间。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># config/application.rb</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># default, store utc time</span>
</span><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">active_record</span><span class="o">.</span><span class="n">default_timezone</span> <span class="o">=</span> <span class="ss">:utc</span>
</span><span class='line'><span class="c1"># store local time</span>
</span><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">active_record</span><span class="o">.</span><span class="n">default_timezone</span> <span class="o">=</span> <span class="ss">:local</span>
</span></code></pre></td></tr></table></div></figure>
<p>到这里就明白为什么数据库里的时间与实际时间相差了八个小时了。所谓的“实际时间”就是本地时间,北京时间,东八区时间,对应的 UTC 时间就是减掉 8 个小时的时间。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># Rails console</span>
</span><span class='line'><span class="o">[</span><span class="mi">5</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'><span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">3</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">WHERE</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="o">=</span> <span class="vg">$1</span> <span class="no">LIMIT</span> <span class="vg">$2</span> <span class="o">[[</span><span class="s2">"id"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span><span class="p">,</span> <span class="o">[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">CST</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># PostgreSQL console</span>
</span><span class='line'><span class="n">pg_development</span><span class="o">></span> <span class="nb">select</span> <span class="n">created_at</span> <span class="n">from</span> <span class="n">bars</span> <span class="n">where</span> <span class="nb">id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'><span class="o">+----------------------------+</span>
</span><span class='line'><span class="o">|</span> <span class="n">created_at</span> <span class="o">|</span>
</span><span class='line'><span class="o">|----------------------------|</span>
</span><span class='line'><span class="o">|</span> <span class="mi">2016</span><span class="o">-</span><span class="mo">06</span><span class="o">-</span><span class="mi">29</span> <span class="mi">15</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span><span class="o">.</span><span class="mi">769707</span> <span class="o">|</span>
</span><span class='line'><span class="o">+----------------------------+</span>
</span></code></pre></td></tr></table></div></figure>
<p>可以看到 Rails 自动对数据库里时间做了处理,返回的时间是带有时区信息的。</p>
<p>Rails 可以从哪些地方获取到时区的信息呢?有两种方式。</p>
<h3>操作系统</h3>
<p>第一种是直接从操作系统获取。操作系统都是有时区配置的,Ruby 的 <code>Time</code> 和 <code>DateTime</code> 类可以获取到操作系统的时区。下面几行命令的输出可以看到时区的影响。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="sx">% ll </span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">localtime</span>
</span><span class='line'><span class="n">lrwxr</span><span class="o">-</span><span class="n">xr</span><span class="o">-</span><span class="n">x</span> <span class="mi">1</span> <span class="n">root</span> <span class="n">wheel</span> <span class="mi">33</span><span class="n">B</span> <span class="mi">7</span> <span class="mi">9</span> <span class="mi">18</span><span class="p">:</span><span class="mi">08</span> <span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">localtime</span> <span class="o">-></span> <span class="sr">/usr/s</span><span class="n">hare</span><span class="o">/</span><span class="n">zoneinfo</span><span class="o">/</span><span class="no">Asia</span><span class="o">/</span><span class="no">Shanghai</span>
</span><span class='line'>
</span><span class='line'><span class="o">[</span><span class="mi">90</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
</span><span class='line'><span class="o">=></span> <span class="mi">2016</span><span class="o">-</span><span class="mo">07</span><span class="o">-</span><span class="mi">10</span> <span class="mi">15</span><span class="p">:</span><span class="mi">12</span><span class="p">:</span><span class="mi">52</span> <span class="o">+</span><span class="mi">0800</span>
</span></code></pre></td></tr></table></div></figure>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="sx">% ll </span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">localtime</span>
</span><span class='line'><span class="n">lrwxr</span><span class="o">-</span><span class="n">xr</span><span class="o">-</span><span class="n">x</span> <span class="mi">1</span> <span class="n">root</span> <span class="n">wheel</span> <span class="mi">30</span><span class="n">B</span> <span class="mi">7</span> <span class="mi">10</span> <span class="mi">16</span><span class="p">:</span><span class="mi">13</span> <span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">localtime</span> <span class="o">-></span> <span class="sr">/usr/s</span><span class="n">hare</span><span class="o">/</span><span class="n">zoneinfo</span><span class="o">/</span><span class="no">Asia</span><span class="o">/</span><span class="no">Tokyo</span>
</span><span class='line'>
</span><span class='line'><span class="o">[</span><span class="mi">93</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
</span><span class='line'><span class="o">=></span> <span class="mi">2016</span><span class="o">-</span><span class="mo">07</span><span class="o">-</span><span class="mi">10</span> <span class="mi">16</span><span class="p">:</span><span class="mi">13</span><span class="p">:</span><span class="mi">57</span> <span class="o">+</span><span class="mi">0900</span>
</span></code></pre></td></tr></table></div></figure>
<p>当 OS X 的时区从东八区改为东九区时,<code>Time.now</code> 返回的时间也相依的发生变化。其实这种获取时区的方式与 Rails 无关,更准确的说这是 Ruby 获取时区的方式。</p>
<h3>Rails</h3>
<p>第二种方式是在是通过配置文件设置 Rails 应用的时区。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># config/application.rb</span>
</span><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">time_zone</span> <span class="o">=</span> <span class="s1">'UTC'</span>
</span><span class='line'>
</span><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">time_zone</span> <span class="o">=</span> <span class="s1">'Beijing'</span>
</span><span class='line'>
</span><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">time_zone</span> <span class="o">=</span> <span class="s1">'Tokyo'</span>
</span></code></pre></td></tr></table></div></figure>
<p><code>config.time_zone</code> 设置 Rails 默认使用的时区,默认值是 ‘UTC’,它会影响到 Rails 里时间的显示,比如:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># config.time_zone = 'UTC'</span>
</span><span class='line'><span class="o">[</span><span class="mi">11</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="mi">2</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">15</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">UTC</span> <span class="o">+</span><span class="mo">00</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># config.time_zone = 'Beijing'</span>
</span><span class='line'><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">CST</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span></code></pre></td></tr></table></div></figure>
<p>可以看到,相同的时间,因为默认时区的不同,显示是不同的。如果我们把 UTC 时间当做绝对时间,设置时区只是让 Rails 把绝对时间显示为对应时区的时间。除了默认的时区设置,还有其他设置时区的方式。</p>
<h4>Time#zone=</h4>
<p>Rails 扩展了 <code>Time</code> 类,增加了 <code>zone=</code> 等方法,<code>config.time_zone = 'Beijing'</code> 实际上就等价于 <code>Time.zone = 'Beijing'</code>,只不过使用 <code>Time#zone=</code> 可以重设默认时区。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># config.time_zone = 'Beijing'</span>
</span><span class='line'><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">4</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">CST</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'>
</span><span class='line'><span class="o">[</span><span class="mi">2</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span> <span class="o">=</span> <span class="s1">'Beijing'</span>
</span><span class='line'><span class="o">=></span> <span class="s2">"Beijing"</span>
</span><span class='line'><span class="o">[</span><span class="mi">3</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">4</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">CST</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'>
</span><span class='line'><span class="o">[</span><span class="mi">4</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span> <span class="o">=</span> <span class="s1">'Singapore'</span>
</span><span class='line'><span class="o">=></span> <span class="s2">"Singapore"</span>
</span><span class='line'><span class="o">[</span><span class="mi">5</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">3</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">SGT</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'>
</span><span class='line'><span class="o">[</span><span class="mi">6</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span> <span class="o">=</span> <span class="s1">'Tokyo'</span>
</span><span class='line'><span class="o">=></span> <span class="s2">"Tokyo"</span>
</span><span class='line'><span class="o">[</span><span class="mi">7</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">3</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Thu</span><span class="p">,</span> <span class="mi">30</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mo">00</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">JST</span> <span class="o">+</span><span class="mi">09</span><span class="p">:</span><span class="mo">00</span>
</span></code></pre></td></tr></table></div></figure>
<h4>ActiveSupport::TimeWithZone#in_time_zone</h4>
<p><code>Time#zone=</code> 修改时区后会影响到所有时间的显示,<code>in_time_zone</code> 只针对一个对象,不会影响其他的时间对象。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="n">t</span> <span class="o">=</span> <span class="no">Bar</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">created_at</span>
</span><span class='line'> <span class="no">Bar</span> <span class="no">Load</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">ms</span><span class="p">)</span> <span class="no">SELECT</span> <span class="s2">"bars"</span><span class="o">.</span><span class="n">*</span> <span class="no">FROM</span> <span class="s2">"bars"</span> <span class="no">ORDER</span> <span class="no">BY</span> <span class="s2">"bars"</span><span class="o">.</span><span class="s2">"id"</span> <span class="no">ASC</span> <span class="no">LIMIT</span> <span class="vg">$1</span> <span class="o">[[</span><span class="s2">"LIMIT"</span><span class="p">,</span> <span class="mi">1</span><span class="o">]]</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">CST</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'><span class="o">[</span><span class="mi">2</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="n">t</span><span class="o">.</span><span class="n">in_time_zone</span><span class="p">(</span><span class="s1">'UTC'</span><span class="p">)</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">15</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">UTC</span> <span class="o">+</span><span class="mo">00</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'><span class="o">[</span><span class="mi">3</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="n">t</span><span class="o">.</span><span class="n">in_time_zone</span><span class="p">(</span><span class="s1">'Singapore'</span><span class="p">)</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">SGT</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'><span class="o">[</span><span class="mi">4</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="n">t</span><span class="o">.</span><span class="n">in_time_zone</span><span class="p">(</span><span class="s1">'Tokyo'</span><span class="p">)</span>
</span><span class='line'><span class="o">=></span> <span class="no">Thu</span><span class="p">,</span> <span class="mi">30</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mo">00</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">JST</span> <span class="o">+</span><span class="mi">09</span><span class="p">:</span><span class="mo">00</span>
</span><span class='line'><span class="o">[</span><span class="mi">5</span><span class="o">]</span> <span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">></span> <span class="n">t</span>
</span><span class='line'><span class="o">=></span> <span class="no">Wed</span><span class="p">,</span> <span class="mi">29</span> <span class="no">Jun</span> <span class="mi">2016</span> <span class="mi">23</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span><span class="mi">25</span> <span class="no">CST</span> <span class="o">+</span><span class="mi">08</span><span class="p">:</span><span class="mo">00</span>
</span></code></pre></td></tr></table></div></figure>
<h2>时区的影响</h2>
<p>目前看来,不管如何设置时区,貌似只会影响时间的显示,毕竟修改时区不会改变绝对时间,但某些场景中时区就显得举足轻重了。比如“每天零点重新计算用户活跃度”,“每周一上午八点发送推荐邮件”,我们发现如果不指明时区,“零点”和“上午八点”这样的时间是无意义的。在类似的场景中,时区是必须提供的一个信息。在一些国际化产品中,用户来自全世界各地,在查看报表等信息时,用户当然希望显示的是自己所在时区的时间。一种解决方式是在用户表中增加一个字段来保存用户的时区,然后用 <code>in_time_zone</code> 或下面的 <code>around_action</code> 将时间显示为用户所在时区的本地时间。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># http://api.rubyonrails.org/classes/Time.html#method-c-zone-3D</span>
</span><span class='line'><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o"><</span> <span class="ss">ActionController</span><span class="p">:</span><span class="ss">:Base</span>
</span><span class='line'> <span class="n">around_filter</span> <span class="ss">:set_time_zone</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">set_time_zone</span>
</span><span class='line'> <span class="k">if</span> <span class="n">logged_in?</span>
</span><span class='line'> <span class="no">Time</span><span class="o">.</span><span class="n">use_zone</span><span class="p">(</span><span class="n">current_user</span><span class="o">.</span><span class="n">time_zone</span><span class="p">)</span> <span class="p">{</span> <span class="k">yield</span> <span class="p">}</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="k">yield</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h2>参考</h2>
<p><a href="http://danilenko.org/2012/7/6/rails_timezones/">The Exhaustive Guide to Rails Time Zones</a></p>
<p><a href="https://robots.thoughtbot.com/its-about-time-zones">It’s About Time (Zones)</a></p>
<p><a href="http://stackoverflow.com/questions/6118779/how-to-change-default-timezone-for-active-record-in-rails">http://stackoverflow.com/questions/6118779/how-to-change-default-timezone-for-active-record-in-rails</a></p>
<p><a href="http://api.rubyonrails.org/classes/Time.html#method-c-zone-3D">http://api.rubyonrails.org/classes/Time.html#method-c-zone-3D</a></p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2016/04/02/letsencrypt/">尝试 Let’s Encrypt</a></h1>
<p class="meta">
<time datetime="2016-04-02T20:22:00+08:00" pubdate data-updated="true">Apr 2<span>nd</span>, 2016</time>
</p>
</header>
<div class="entry-content"><p>在计算机网络的远古时期,HTTP 的设计者一开始并没有在安全方面做太多的考虑,他们把 HTTP 设计为一个明文传输的文本协议,简单、直观。HTTP 出现后互联网经历了爆炸性的成长,HTTP 成为了使用最广泛的一种应用层网络协议,这与它简单直观的特点不无关系。然而,在互联网高速发展了几十年后,支撑 HTTP 广泛应用的几个特性却正在慢慢制约互联网的继续发展。其中一个特性,明文传输,越来越不适合存在于当今的互联网环境。明文传输,意味着在数据链路上的任何人都有可能会窥探和篡改数据,在数据往返的途中,路由器、公共 WiFi、电信运营商、GFW 等都有可能造成潜在的危害。当前人们对互联网安全性的要求越来越高,使用加密的传输协议势在必行。</p>
<p>好在有 HTTPS,它在 HTTP 的基础上增加了一层加密的协议,应用层的代码几乎不需要任何修改,只要部署好相关的设施就能无缝迁移到安全的 HTTPS 协议了。但是部署 HTTPS 需要向证书认证机构申请证书,这对个人来说是一件非常繁琐的事情,而且证书通常需要购买,更增加了个人网站使用 HTTPS 的难度。</p>
<p>然而在 2015 年,情况发生了变化,一家新的证书机构 <a href="https://letsencrypt.org/">Let’s Encrypt</a> 开始提供免费、自动化的证书服务。它的官网是这么描述自己的:</p>
<blockquote><p>Let’s Encrypt is a new Certificate Authority:</p>
<p>It’s free, automated, and open.</p></blockquote>
<p>免费,Let’s Encrypt 提供的证书是完全免费的,完全不需要任何支付费用。自动化的,使用它提供的工具几部操作就可以完成证书的获取,更进一步,还可以把证书的获取、更新、部署通过程序完全自动化。开放的,整个服务开发与更新都是开放的。当然,Let’s Encrypt 提供的证书是安全的。</p>
<h2>获取证书</h2>
<p>在刚了解 Let’s Encrypt 时,我以为需要到官网注册账号,之后才能获取到证书。后来发现完全不需要,只要证明证书绑定的域名属于你自己就可以了,非常简便。</p>
<p>Let’s Encrypt 提供了工具来实现证书的获取和更新,可以通过 Git 下载:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>git clone https://github.com/letsencrypt/letsencrypt
</span><span class='line'><span class="nv">$ </span><span class="nb">cd </span>letsencrypt
</span><span class='line'><span class="nv">$ </span>./letsencrypt-auto --help
</span></code></pre></td></tr></table></div></figure>
<p>运行 <code>letsencrypt-auto</code> 会自动安装依赖包并且把自身升级到最新版,所以第一次执行上面最后一条命令会有一点慢。执行过命令后会创建 <code>/etc/letsencrypt/</code> 目录,证书以及其他的数据都会保存在这个目录,因此执行命令需要有 root 权限。</p>
<p>当 <code>letsencrypt-auto</code> 把依赖安装和升级都做完后就可以使用了。<code>letsencrypt-auto</code> 提供了好几种方式来获取证书,下面是 <code>letsencrypt-auto --help</code> 输出的一部分,描述了提供的几个子命令。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'> <span class="o">(</span>default<span class="o">)</span> run Obtain & install a cert in your current webserver
</span><span class='line'> certonly Obtain cert, but <span class="k">do </span>not install it <span class="o">(</span>aka <span class="s2">"auth"</span><span class="o">)</span>
</span><span class='line'> install Install a previously obtained cert in a server
</span><span class='line'> renew Renew previously obtained certs that are near expiry
</span><span class='line'> revoke Revoke a previously obtained certificate
</span><span class='line'> rollback Rollback server configuration changes made during install
</span><span class='line'> config_changes Show changes made to server config during installation
</span><span class='line'> plugins Display information about installed plugins
</span></code></pre></td></tr></table></div></figure>
<p><code>certonly</code> 子命令只用来获取证书,证书的安装,也就是在 Web Server 里的配置需要手动完成。<code>renew</code> 更新已经获取到的证书,获取到的证书只有 90 天有效期,通过这个命令可以将证书的剩余有效期更新为 90 天。</p>
<p><code>letsencrypt-auto</code> 使用插件机制实现证书的获取和安装,下面介绍自带的三种插件。</p>
<h3>Apache</h3>
<p><code>letsencrypt-auto</code> 提供了针对 Apache 的自动获取和安装插件,只要一条命令 <code>letsencrypt-auto --apache</code> 就完成获取和安装,但是我使用的并不是 Apache,因此无法验证这种方法。</p>
<h3>Webroot</h3>
<p><code>letsencrypt-auto</code> 提供的第二种获取证书的插件叫 Webroot。这种方式需要配置服务器上已运行的 Web Server 来实现。比如 Nginx,需要在配置文件里添加下面的配置,使得 <code>/.well-known/*</code> 这样的路径可以被访问到。修改好配置文件后不要忘记 <code>restart</code> 或 <code>reload</code> Nginx。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'> location ~ /.well-known <span class="o">{</span>
</span><span class='line'> allow all;
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>第二步,需要搞清楚 Web Server 提供静态文件的根路径。基本是对应 Nginx <code>root</code> 指令指定的位置,比如 <code>/usr/share/nginx/html</code> 或 <code>/var/www/</code>,需要根据具体的配置文件来确定,我们假设这个路径叫 <code>$WEBROOT_PATH</code>,同时假设证书绑定的域名叫 <code>$DOMAIN</code> 和 <code>$WWW_DOMAIN</code>。</p>
<p>然后执行下面的命令就可以获取到证书了。证书以及其他数据存储在 <code>/etc/letsencrypt/</code> 目录。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>./letsencrypt-auto certonly --webroot --webroot-path <span class="nv">$WEBROOT_PATH</span> -d <span class="nv">$DOMAIN</span> -d <span class="nv">$WWW_DOMAIN</span>
</span></code></pre></td></tr></table></div></figure>
<p>Webroot 插件的原理是配置已有的 Web Server,使 <code>/.well-known/*</code> 这样的路径可以从外部访问,同时在 Web Server 的根路径放一个 <code>.well-known/xxx...</code> 文件。这个文件是在执行上面的命令时生成的,然后把访问路径告诉 Let’s Encrypt 的服务器,Let’s Encrypt 服务器访问后就可以验证待绑定域名的有效性。所以在执行完上面的命令后,Web Server 的日志里应该会有几条类似这样的访问记录。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="s2">"GET /.well-known/acme-challenge/kiEt5Rn8rAQqZBQ9-ve6-xxxxxx HTTP/1.1"</span> <span class="s2">"Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"</span>
</span></code></pre></td></tr></table></div></figure>
<h3>Standalone</h3>
<p>Webroot 需要配置已有的 Web Server,步骤稍显繁琐,standalone 插件可以把这些事情自动化,但前提是如果已有 Web Server 在监听 80 端口,需要暂时停掉,否则 standalone 插件不能启动自带的服务器程序。获取到的证书同样存储在 <code>/etc/letsencrypt/</code>。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>./letsencrypt-auto certonly --standalone -d <span class="nv">$DOMAIN</span> -d <span class="nv">$WWW_DOMAIN</span>
</span></code></pre></td></tr></table></div></figure>
<h2>使用证书配置 HTTPS</h2>
<p>配置 HTTPS 已经与 Let’s Encrypt 没有关系了。比如 Nginx 可以使用下面的片段配置 HTTPS,其中的域名需要修改成自己的。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'> listen 443 ssl;
</span><span class='line'>
</span><span class='line'> server_name example.com www.example.com;
</span><span class='line'>
</span><span class='line'> ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
</span><span class='line'> ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
</span></code></pre></td></tr></table></div></figure>
<h2>续签证书</h2>
<p>上面提到过,获取到的证书有效期只有 90 天,因此需要在过期之前续签,将证书有效期重新设置为 90 天。使用 renew 子命令实现续签。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>./letsencrypt-auto renew
</span></code></pre></td></tr></table></div></figure>
<p>如果想要实现自动续签,只要在一定时间间隔内重新执行上面的命令就可以了。网上的大部分教程使用 Linux 的 Cron 实现自动续签。</p>
<h2>参考</h2>
<p><a href="https://letsencrypt.org/">Homepage</a></p>
<p><a href="https://letsencrypt.org/getting-started/">Getting Started</a></p>
<p><a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04">Configure Nginx on Ubuntu 14.04</a></p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/12/12/promise-in-javascript/">Promise in JavaScript</a></h1>
<p class="meta">
<time datetime="2015-12-12T19:59:00+08:00" pubdate data-updated="true">Dec 12<span>th</span>, 2015</time>
</p>
</header>
<div class="entry-content"><h2>Promise</h2>
<p>JavaScript 的并发编程模型是依赖回调函数的异步事件驱动模型。这种模型通过只使用单线程避免了多线程模型的诸多弊端,同时还可以充分利用单核 CPU,达到不错的性能表现。但是如果业务逻辑比较复杂,大量的异步回调函数会使代码非常难读难调试。Promise 是对回调函数的进一步抽象,力图在保留原有优势的情况下解决回调函数带来的问题。</p>
<h2>Promise in jQuery</h2>
<p>jQuery 可能是使用最多的 JavaScript 库,jQuery 的 Ajax 部分显然是异步的、依赖回调函数的,自然也就有上面说的各种问题。jQuery 从 1.5 版本开始引入了 Deferred 对象,Deferred 对象和相关的 Promise 对象是 jQuery 对 Promise 的实现。Deferred 包含两类方法,一类是修改内部状态的,一类是注册回调函数的,Promise 隐藏了 Deffered 的修改内部状态的方法,只保留了注册回调函数的接口。jQuery 1.5 的 Ajax 全部使用 Deferred 对象重写了,所以很多人可能已经通过 Ajax 操作使用过 Deferred 对象了。下面的例子是 Ajax 的两种写法:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="c1">// callback</span>
</span><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
</span><span class='line'> <span class="nx">url</span><span class="o">:</span> <span class="s1">'/foo'</span><span class="p">,</span>
</span><span class='line'> <span class="nx">success</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="nx">error</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'error'</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="c1">// Promise object</span>
</span><span class='line'><span class="nx">xhr</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
</span><span class='line'> <span class="nx">url</span><span class="o">:</span> <span class="s1">'/foo'</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">xhr</span><span class="p">.</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">xhr</span><span class="p">.</span><span class="nx">fail</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'error'</span><span class="p">);</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// chainable Promise object</span>
</span><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
</span><span class='line'> <span class="nx">url</span><span class="o">:</span> <span class="s1">'/foo'</span>
</span><span class='line'><span class="p">}).</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span><span class='line'><span class="p">}).</span><span class="nx">fail</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'error'</span><span class="p">);</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p><code>jQuery.ajax</code> 的返回值是一个 Promise 对象,<code>done</code> 和 <code>fail</code> 都是 Promise 对象的方法,而且返回值还是 Promise 对象自身。两种写法看上去好像差不多,<code>done</code> 对应 <code>success</code>,<code>fail</code> 对应 <code>error</code>。但是 Promise 对象的写法可以把把回调单独拿出来写,同时又因为可以使用链式写法,可读性上貌似好了一些。但是使用 Promise 的写法还有其他好处。</p>
<p>首先,Promise 对象的 <code>done</code> 和 <code>fail</code> 方法可以调用多次,也就是可以自由关联多个回调函数,传统写法如果想关联多个回调,只能把多个函数用一个函数包裹后才可以。</p>
<p>其次,我认为是最重要的,Promise 对象可以解耦事件注册和事件触发的顺序。在传统的 JavaScript 事件编程中,事件注册一定要在事件触发之前,否则可能会漏掉事件。看下面的例子:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="c1">// correct</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">httpRequest</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'><span class="nx">httpRequest</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="nx">httpRequest</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'/foo/bar'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span><span class='line'><span class="nx">httpRequest</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="c1">// wrong!</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">httpRequest</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'><span class="nx">httpRequest</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'/foo/bar'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span><span class='line'><span class="nx">httpRequest</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="nx">httpRequest</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>
<p>第二段代码是错误的,在 <code>send</code> 之后再使用 <code>onreadystatechange</code> 注册回调函数有可能会漏掉一些事件。jQuery 的 <code>ajax</code> 函数通过把回调限制为参数的一部分来避免这个问题。</p>
<p>Promise 则不一定需要保证这样的顺序,因为 Promise 对象内部保存了事件的状态,所以即使注册回调函数发生在事件触发之后,注册的回调函数仍然可以执行。下面的例子演示了这个过程。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">slow_func</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">1000000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="nx">xhr</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
</span><span class='line'> <span class="nx">url</span><span class="o">:</span> <span class="s1">'/foo'</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">slow_func</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// xhr completed here!</span>
</span><span class='line'>
</span><span class='line'><span class="nx">slow_func</span><span class="p">();</span>