-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
3059 lines (2582 loc) · 275 KB
/
Copy pathatom.xml
File metadata and controls
3059 lines (2582 loc) · 275 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
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Feng erdong's Blog]]></title>
<link href="http://fedcuit.github.com/atom.xml" rel="self"/>
<link href="http://fedcuit.github.com/"/>
<updated>2016-03-14T22:27:33+08:00</updated>
<id>http://fedcuit.github.com/</id>
<author>
<name><![CDATA[Feng erdong]]></name>
<email><![CDATA[fedcuit@gmail.com]]></email>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[在JS string.replace() 中使用捕获组]]></title>
<link href="http://fedcuit.github.com/blog/2016/03/14/js-string-replace/"/>
<updated>2016-03-14T21:34:00+08:00</updated>
<id>http://fedcuit.github.com/blog/2016/03/14/js-string-replace</id>
<content type="html"><![CDATA[<p>上周在写代码的时候要在<code>componentWillUpdate</code>方法中判断一下当前<code>state</code>/<code>props</code>有没有改变, 这个判断其实跟<code>PureRenderMixin</code>干的是完全一样的事情, 但是<code>PureRenderMixin</code>是在<code>shouldComponentUpdate</code>这个life cycle method里执行的, 所以并不能直接拿来用. 跑去看了一下<code>PureRenderMixin</code>的实现, 发现它对<code>state</code>/<code>props</code>的比较是依赖于一个<code>shallowEqual</code>方法, 而这个方法在<code>fbjs</code>里面, <a href="https://github.com/facebook/fbjs">fbjs</a>是Facebook维护的一个实用类库的集合, 所以今天决定读一读这个项目, 希望能从中学到一些新的东西.</p>
<p>那先来分享一下学到的第一个新东西吧.</p>
<h2><code>String.replace()</code></h2>
<p>之前对这个函数的认识比较简单, 只知道可能将指定的字符串或pattern替换成另外一个字符串, 如:</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'AlphaGo vs Lee Sedol'</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">'Go'</span><span class="p">,</span> <span class="s1">'Google'</span><span class="p">))</span>
</span><span class='line'><span class="c1">// "AlphaGoogle vs Lee Sedol"</span>
</span></code></pre></td></tr></table></div></figure>
<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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'188-0000-0000'</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/-/</span><span class="p">,</span> <span class="s1">'+'</span><span class="p">))</span>
</span><span class='line'><span class="c1">// "188+0000+0000"</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="s1">'188-0000-0000'</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/-/g</span><span class="p">,</span> <span class="s1">'+'</span><span class="p">))</span>
</span><span class='line'><span class="c1">// "188+0000+0000"</span>
</span></code></pre></td></tr></table></div></figure>
<p>需要注意的是, 正则表达式没有使用<code>g</code>选项的时候, <code>replace</code>只会替换第一处匹配.</p>
<p>今天学到的高级用法是<code>replace</code>函数还可以用一个函数来返回替换量, 而且这个函数的参数是整个匹配还有这个匹配中的所有捕获组, 比如<code>fbjs</code>中的<a href="https://github.com/facebook/fbjs/blob/master/src/core/camelize.js#L24">camelize</a>函数:</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kr">const</span> <span class="nx">_hyphenPattern</span> <span class="o">=</span> <span class="sr">/-(.)/g</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * Camelcases a hyphenated string, for example:</span>
</span><span class='line'><span class="cm"> *</span>
</span><span class='line'><span class="cm"> * > camelize('background-color')</span>
</span><span class='line'><span class="cm"> * < "backgroundColor"</span>
</span><span class='line'><span class="cm"> *</span>
</span><span class='line'><span class="cm"> * @param {string} string</span>
</span><span class='line'><span class="cm"> * @return {string}</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">camelize</span><span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">string</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">_hyphenPattern</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">character</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">character</span><span class="p">.</span><span class="nx">toUpperCase</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>
<p>这里 <code>_hyphenPattern</code> 用来匹配连字符写法, 并将连字符写法替换成驼峰写法, 我们无法通过简单将<code>-</code>替换成空白字符来实现, 因为我们还需要将<code>-</code>后面的字母大写, 这个时候可以接受捕获组的函数上可以出场了. 在 <code>_hyphenPattern</code> 的定义里面, <code>-</code>后面的那个字母是被定义成一个捕获组, 它会作为第二个参数传入replacement function(第一个参数是匹配的字符串), replacement function将这个字母大写之后返回, 就实现了将<code>-</code> + 任意字母 替换成了一个大写之后的字母的.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[JSX里的那些个坑]]></title>
<link href="http://fedcuit.github.com/blog/2015/07/23/jsx-gotchas/"/>
<updated>2015-07-23T21:52:00+08:00</updated>
<id>http://fedcuit.github.com/blog/2015/07/23/jsx-gotchas</id>
<content type="html"><![CDATA[<p>最近在项目上使用<code>Backbone</code> + <code>React</code>作为前端框架, Backbone主要是用来作为<code>Model</code>来与后台的Restful API进行交互, React则负责<code>View</code>, 也算是发挥了各自的优势: Backbone的Model和Collection只需要指定对应Resource的url, 就可以直接读取/更新/删除记录, 而不需要手动去发送对应的AJAX请求, 另外借助于寄生在Model/Collection上的<code>underscore</code>方法, 操作数据也很方便; React接收Backbone的Model/Collection作为<code>state</code>或<code>props</code>, 通过<code>Virtual DOM</code>来高效的DOM更新, 与其他的View技术相比较, React功能更单一, 概念更少更易理解, 而且有更高的性能. 这一切看下来都是那么的美好, 但是使用React还是在它的代价的, 代价就是<code>JSX</code>.</p>
<p>简单讲, <code>JSX</code>也就是一段写在JavaScript中的XML, 它是由React提供的一种更简洁的生成Virtual DOM的方法, 通过使用<code>JSX</code>我们可以像书写<code>HTML</code>一样来编写React Component, 然后再使用对应的编译器将<code>JSX</code>定义的Component Tree<em>翻译</em>成不易读的<code>React.createElement(...)</code> (Virtual DOM就是由<code>React.createElemnt()</code>创建出来的)</p>
<p>正是因为<code>JSX</code>代码会最终被翻译成对应的Javascript代码, 所以在书写<code>JSX</code>代码的时候, 虽然我们说他的形式像是<code>HTML</code>一样, 但是我们并不能完全遵照HTML规范来书写, 在<code>JSX</code>中使用HTML的属性还是有一些限制(坑)的:</p>
<h2>避免出现Javascript关键字</h2>
<p>因为<code>JSX</code>会被编译成JS代码供浏览器执行, 所以如果在<code>JSX</code>中使用了Javascript的关键字的话, 那么很可能导致生成的代码无法被正确解释.(在IE上这个问题格外严重, 但在强大的Chrome上, 这种情况可以被很智能地处理).</p>
<p>一看下面的例子就明白了:</p>
<p>这段<code>JSX</code>代码生成一个表单, 其中有一个Label和对应的Input用来输入用户的名字,</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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><form></span>
</span><span class='line'> <span class="nt"><label</span> <span class="na">for=</span><span class="s">"name"</span><span class="nt">></span>Your name:<span class="nt"></label></span>
</span><span class='line'> <span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
</span><span class='line'><span class="nt"></form></span>
</span></code></pre></td></tr></table></div></figure>
<p>根据它编译生成的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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"form"</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span>
</span><span class='line'> <span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"label"</span><span class="p">,</span> <span class="p">{</span>
</span><span class='line'> <span class="k">for</span><span class="o">:</span> <span class="s2">"name"</span>
</span><span class='line'> <span class="p">},</span> <span class="s2">"Your name:"</span><span class="p">),</span>
</span><span class='line'> <span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"input"</span><span class="p">,</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">type</span><span class="o">:</span> <span class="s2">"text"</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>
<p>通过比较我们基本上可以总结出这个<em>翻译</em>的过程, 对于一个<code>JSX Tag</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='html'><span class='line'><span class="nt"><tagName</span> <span class="na">attr1=</span><span class="s">val1</span> <span class="na">attr2=</span><span class="s">val2</span><span class="nt">></span>
</span><span class='line'> [childrenComponents]
</span><span class='line'><span class="nt"></tagName></span>
</span></code></pre></td></tr></table></div></figure>
<p>它生成的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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"tagName"</span><span class="p">,</span> <span class="p">{</span><span class="nx">attr1</span><span class="o">:</span> <span class="nx">val1</span><span class="p">,</span> <span class="nx">attr2</span><span class="o">:</span> <span class="nx">val2</span><span class="p">},</span> <span class="p">[</span><span class="nx">childrenReactComponents</span><span class="p">]);</span>
</span></code></pre></td></tr></table></div></figure>
<p>可以看出其中<code>TagName</code>是最安全的, 因为它在生成的JS中是一个字符串, 但是HTML Attribute就没有这么幸运了, 如果它碰巧是一个Javascript关键字, 那么恭喜你, 你掉到坑里去了. 比如上例中<code>label</code>上面的<code>for</code>属性在被编译后的Javascript代码中就很可能被浏览器当作是<em>循环</em>的那个for, 从而报出一个语法错误出来.</p>
<p>为了解决类似这种问题, React为这些”不幸”的属性定义了对应的”替身”, 它们中的一些是(不完全版):</p>
<table>
<thead>
<tr>
<th align="left">Native HTML Attribute </th>
<th align="left"> React Version </th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>for</code> </td>
<td align="left"> <code>htmlFor</code> </td>
</tr>
<tr>
<td align="left"><code>class</code> </td>
<td align="left"> <code>className</code> </td>
</tr>
</tbody>
</table>
<h2>另类的inlineStyle</h2>
<p>inline style估计是最让React的初学者头痛的问题了, 如果我们按照HTML规范来使用的话, 是无法将想要的样式apply到元素上的, 至于为什么, 我们看一下由JSX编译成的JS代码就明白了:</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='html'><span class='line'><span class="nt"><div</span> <span class="na">style=</span><span class="s">"color: yellow"</span><span class="nt">></span>Hello World<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>我们在JSX中定义了一个DIV元素, 并且想通过inline style将它里面的文字颜色设为<em>yellow</em>, 然而这个DIV甚至都无法被渲染到页面中, 因为React认为它是不合法的:</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='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="p">{</span><span class="nx">style</span><span class="o">:</span> <span class="s2">"color: yellow"</span><span class="p">},</span> <span class="s2">"Hello World"</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>在上面这段生成的JS里面, 用于设置<code>style</code>的值是一个字符串, 这显然是无法工作的, 因为<code>style</code>这个属性与其他属性不一样, 它比较特殊, 一般的属性的值都是一个普通的字符串, 比如<code>type="text"</code>, <code>name="fieldName"</code>, 这些属性值都是可以被浏览器直接使用的, 而style它传递的是一组样式属性, 它在这里已经不是一般的字符串, 而是可以被解析的一个样式对象, 说到这里, 估计你也猜到了, 我们不能直接使用一个普通字符串给style, 我们要传递的应该是一个JS对象(JSON)才对:</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='html'><span class='line'><span class="nt"><div</span> <span class="na">style=</span><span class="s">"{color: yellow}"</span><span class="nt">></span>Hello World<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>但是将代码改成这样之后还是无法将这个DIV渲染出来, 再次查看生成的JS代码:</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='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="p">{</span><span class="nx">style</span><span class="o">:</span> <span class="s2">"{color: yellow}"</span><span class="p">},</span> <span class="s2">"Hello World"</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>style的值还是一个字符串! 这里正好引出了JSX的一条很重要的规则, 即被放在<code>{}</code>中的都作为Javascript代码来处理, 但是如果<code>{}</code>被引号包围起来, 那么它当作一个字符串处理. 这可能是JSX让人感觉比较困惑的地方, 当在JSX中使用<code>{}</code>来引用JS变量的时候, 很容易让人感觉<code>JSX</code>似乎是模板引擎, 而对于模板引擎来说(比如Freemarker, JSP等), 它们会将所模板中所有的变量都替换成对应的值, 不管它们是不是在一对双引号中:</p>
<p>如在Freemarker中, 如果变量 <code>userName</code>的值是 ‘Erdong FENG’, 使用的模板是</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='html'><span class='line'><span class="nt"><div</span> <span class="na">data-name=</span><span class="s">"${userName}"</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><div</span> <span class="na">data-name=</span><span class="s">"Erdong FENG"</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>然而在JSX中并不是这样的, 因为<em><code>JSX不是模板引擎</code></em> (当然了, 大多数时候JSX表现的还是像一个模板引擎, 比如在一个DIV的文字结点中, 但是在一个Tag的属性值中使用变量的时候, 情况完全不一样)</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='html'><span class='line'><span class="nt"><div</span> <span class="na">data-name=</span><span class="s">"{userName}"</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="p">{</span><span class="s2">"data-name"</span><span class="o">:</span> <span class="s2">"{userName}"</span><span class="p">},</span> <span class="s2">"Welcome to our website"</span><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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><div</span> <span class="na">data-name=</span><span class="s">"{userName}"</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>当然了, 我们在innerHTML的位置还是可以将JSX当作模板引擎来使用的:</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='html'><span class='line'><span class="nt"><div></span>Welcome to our website, "{userName}"<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>最终的JS跟HTML如下:</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='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="s2">"Welcome to our website, \""</span><span class="p">,</span> <span class="nx">userName</span><span class="p">,</span> <span class="s2">"\""</span><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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><div></span>
</span><span class='line'> <span class="nt"><span></span>Welcome to our website, "<span class="nt"></span></span>
</span><span class='line'> <span class="nt"><span></span>Erdong FENG<span class="nt"></span></span>
</span><span class='line'> <span class="nt"><span></span>"<span class="nt"></span></span>
</span><span class='line'><span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>一下子跑得有点偏了, 再回顾一下, 在JSX中, <code>{}</code>中的东西是当作JS来处理的, 因为在生成的HTML中这部分会被对应的JS表达式的值替换, 但是如果是在属性值中使用<code>{}</code>, 那么就一定不能用引号将它括起来了, 不然的话它就会当作字符串来处理!</p>
<p>再回到inline style的问题上, 基于上面的分析, 我们这次不给属性值加引号了</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='html'><span class='line'><span class="nt"><div</span> <span class="na">style=</span><span class="s">{color:</span> <span class="na">yellow</span><span class="err">}</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>;
</span></code></pre></td></tr></table></div></figure>
<p>再编译一次, 这次直接就语法错误了, 因为<code>{}</code>里面的部分是被当作Javscript来处理的, 而<code>color: yellow</code>根本不是合法的JS语句, 再回顾一下, 我们这里需要的属性值是一个JSON, 那么应该是<code>{color: yellow}</code>才对, 再将这个JSON放到<code>{}</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='html'><span class='line'><span class="nt"><div</span> <span class="na">style=</span><span class="s">{</span> <span class="err">{</span><span class="na">color:</span> <span class="na">yellow</span><span class="err">}</span> <span class="err">}</span><span class="nt">></span>Welcome to our website<span class="nt"></div></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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="p">{</span><span class="nx">style</span><span class="o">:</span> <span class="p">{</span><span class="nx">color</span><span class="o">:</span> <span class="nx">yellow</span><span class="p">}},</span> <span class="s2">"Welcome to our website"</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>怎么还是有问题?! 原来是因为yellow被当作一个JS变量来解析, 但是却没有对应的变量存在, 所以这时候又引出了在JSX中使用inline style的另一条规则, 对于这些非字面量的值, 需要将他们用引号括起来, 表示他们是字符串:</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='html'><span class='line'><span class="nt"><div</span> <span class="na">style=</span><span class="s">{</span> <span class="err">{</span><span class="na">color:</span> <span class="err">'</span><span class="na">yellow</span><span class="err">'}</span> <span class="err">}</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>这样就可以了!</p>
<p>除此之外, 还有另一个需要注意的地方, 如果style的属性名中含有连字符<code>-</code>, 也需要做适当转换的, 因为属性名在编译后的JS代码中是JSON中的key, key要适合JS的变量名的定义, 因此是不能使用连字符<code>-</code>的, React的解决办法是使用它的camelPattern, 好的, 讲完了.</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='html'><span class='line'><span class="nt"><div</span> <span class="na">style=</span><span class="s">{</span> <span class="err">{</span><span class="na">color:</span> <span class="err">'</span><span class="na">yellow</span><span class="err">',</span> <span class="na">backgroundColor:</span> <span class="err">'</span><span class="na">green</span><span class="err">'}</span> <span class="err">}</span><span class="nt">></span>Welcome to our website<span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[为什么你的Angular代码很难测试]]></title>
<link href="http://fedcuit.github.com/blog/2015/06/11/angular-unit-test/"/>
<updated>2015-06-11T15:56:00+08:00</updated>
<id>http://fedcuit.github.com/blog/2015/06/11/angular-unit-test</id>
<content type="html"><![CDATA[<p>Angular推出有好几年的时候了, 跟其他的MV*框架相比, 它的<code>双向绑定</code>, <code>无须显式声明Model</code>, <code>模块管理</code>, <code>依赖注入</code>等特点都给Web应用开发带来了极大的便利, 另外, 借助于它众多强大的原生<code>directive</code>, 我们几乎可以避免麻烦的DOM操作了, 除了这些, Angular还有一个很大的亮点, 那就是高度的可测试性.</p>
<p>今天的Web开发已经不同往日, 更多的交互与逻辑都需要在前端完成, 有时候, 前端的代码量甚至在后端之上. 怎么去保证如此多的前端逻辑不被破坏, 依赖于功能测试? 这显示不现实, 功能测试很耗时, 而且它的创建成本较高, 所以通常只用它来覆盖最基本的那部分逻辑, 另一方面, 功能测试是依赖于流程的, 如果你想验证购买页面上的某个前端逻辑, 那么你就不得不一路从产品详情页面老老实实点过来, 反馈时间太长了, 可能你要等一分多钟才知道某个功能出错了, 我们自然不想把宝贵的开发时间浪费在等待上.</p>
<p>我在过去一段比较长的时候里都在项目上使用Angular, 在感受到Angular带来的便利的同时, 也饱受了Angular测试的折磨, 因为我一直觉得Angular的单元测试很难写, 跟JUnit + Mockito比起来, Angular代码的单元测试真是感觉写起来不得心应手, 更别说用TDD的方式来驱动开发.</p>
<p>我一直在思考为什么Angular社区说Angular的测试性很高, 但是在项目上实现用起来却是另一番境地. 经过分析项目上的代码, 我觉得要想驱动测试开发Angular代码, 那么其实是对你的Angular代码提出了比较高的要求, 你要遵循Angular的风格来开发你的应用, 只有你了解了其中的思想, 你的测试写起来才会轻松.</p>
<p>如果你已经使用Angular有一段时间了, 但是还没有读过这篇文章, 那么我强烈推荐你去读一下: <a href="http://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background">Thinking in Angular</a>.</p>
<p>先来看一看怎么样的Angular代码才是苗正根红的Angular代码.</p>
<ul>
<li><strong>避免使用任何的DOM操作</strong></li>
</ul>
<p>像DOM操作这样的脏活累活都应该交给Angular的原生<code>directive</code>去做, 我们的Angular代码应该只处理与DOM无关的业务逻辑. 来看一个简单的例子, 我们想创建一个简单的邮箱地址验证的directive, 它要实现的功能是, 当焦点从邮箱地址输入框移出的时候, 对输入框中的邮箱地址进行验证, 如果验证失败, 则向输入框添加一个样式表示输入的地址不合法, 比较糟糕的实现可能是这样的</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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><label</span> <span class="na">for=</span><span class="s">"email"</span><span class="nt">></span>Your email:<span class="nt"></label></span>
</span><span class='line'><span class="nt"><input</span> <span class="na">id=</span><span class="s">"email"</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">ng-model=</span><span class="s">"email"</span> <span class="na">validate-on-blur</span><span class="nt">/></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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"TheNG"</span><span class="p">,</span> <span class="p">[])</span>
</span><span class='line'> <span class="p">.</span><span class="nx">directive</span><span class="p">(</span><span class="s2">"validateOnBlur"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">link</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">scope</span><span class="p">,</span> <span class="nx">element</span><span class="p">,</span> <span class="nx">attrs</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">validate</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">email</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">'@gmail.com'</span><span class="p">)</span> <span class="o">!==</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><span class='line'> <span class="nx">element</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'blur'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">validate</span><span class="p">(</span><span class="nx">scope</span><span class="p">.</span><span class="nx">email</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">element</span><span class="p">.</span><span class="nx">addClass</span><span class="p">(</span><span class="s1">'error-box'</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 class="p">}</span>
</span><span class='line'> <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>
<p>上面的代码应该可以满足我们的要求(验证逻辑因为不是我们关注的重点, 所以并不完善), 而且这个directive实现起来也挺简单的, 但是现在让我们一起来分析一下为什么我们认为这种写法是比较糟糕的.</p>
<p>最简单的办法就是在你的directive里面去找所有与DOM操作相关的代码.</p>
<p>首先看到的就是<code>on()</code>这个事件监听器. 完全没有必要自己去监听发生在被directive修饰的元素上的事件, angular有一整套的原生directive来干这个事情, 这里正确的做法应该是使用<code>ng-blur</code>来处理<code>blur</code>事件.</p>
<p>下一个有问题的地方就是<code>addClass()</code>, angular除了提供了事件监听相关的directive外, 也提供了操作元素本身属性的directive, <code>ng-class</code>就可以用来替换<code>addClass()</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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><label</span> <span class="na">for=</span><span class="s">"email"</span><span class="nt">></span>Your email:<span class="nt"></label></span>
</span><span class='line'><span class="nt"><input</span> <span class="na">id=</span><span class="s">"email"</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">ng-model=</span><span class="s">"email"</span> <span class="na">ng-blur=</span><span class="s">"validate()"</span> <span class="na">ng-class=</span><span class="s">"{'error-box': !isValid}"</span> <span class="na">validate-on-blur</span><span class="nt">/></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='javascript'><span class='line'><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"TheNG"</span><span class="p">,</span> <span class="p">[])</span>
</span><span class='line'> <span class="p">.</span><span class="nx">directive</span><span class="p">(</span><span class="s2">"validateOnBlur"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">link</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">scope</span><span class="p">,</span> <span class="nx">element</span><span class="p">,</span> <span class="nx">attrs</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">scope</span><span class="p">.</span><span class="nx">isValid</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span><span class='line'> <span class="nx">scope</span><span class="p">.</span><span class="nx">validate</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">scope</span><span class="p">.</span><span class="nx">isValid</span> <span class="o">=</span> <span class="nx">scope</span><span class="p">.</span><span class="nx">email</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">'@gmail.com'</span><span class="p">)</span> <span class="o">!==</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 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>
<p>比较一下这两个版本的实现, 是不是修改后的版本更简短, 更容易理解一些. 在新的版本里面, 我们只处理了业务逻辑, 即判断一个邮箱地址是否合法, 至于何时触发验证, 验证失败或成功之后应该有怎样的样式, 我们都统统交给了angular原生directive去处理了.</p>
<p>从测试的角度来看, 如果想给第一个版本的实现写单元测试, 那么要准备和验证的东西都很多, 我们需要设法去触发对应元素的<code>blur</code>事件, 然后再验证这个元素上是否添加了<code>error-box</code>这个class, 根据我的经验, 有时候为了验证这些DOM更新, 你还不得不创建真实的DOM结构添加到DOM tree上去, 又增加了一部分工作量.</p>
<p>而版本二就简单多了, 只定义了一个Model值<code>isValid</code>来标识当前的邮箱地址是否合法, <code>validate()</code>方法会在每次失焦之后自动执行, 要为它添加单元测试, 则只需要调用一下它的<code>validate()</code>方法, 然后验证<code>isValid</code>的值就可以了. SO EASY!~</p>
<ul>
<li><strong>将所有第三方服务封装成Service</strong></li>
</ul>
<p>一个Web项目中总是无法避免地要使用一些第三方的服务, 这里讨论的主要是前端的一些第三方服务, 比如在线客服, 站点统计等, 这些代码都在我们的控制之外, 大多数时候下都是从服务提供商的服务器上下载下来的, 而我们需要在业务代码中调用这些代码.</p>
<p>如果我们每次都是赤裸裸地以全局变量的形式来使用这些服务, 那么造成的问题就是这样的代码很难测试, 因为这些代码是不存在于我们的代码库中的, 而且内容应该也是不定时更新的, 大多数情况很多人会因为这些原因放弃到对这类操作的测试.</p>
<p>假设我们现在需要在某些动作发生之后调用一个第三方服务, 这个第三方服务叫做<code>serviceLoadedFromExternal</code>, 它提供了一个API叫做<code>makeServiceCall</code>, 如果直接使用这个API, 那么在测试中很难去验证这个服务被执行了(因为在单元测试环境中这个服务根本不存在), 但是如果我们将这个服务包装成一个angular service, 那么就可以在测试中轻易地将它替换成一个mock对象, 然后验证这个mock对象上的方法被调用了就可以了.</p>
<p>比较下面的两段代码:</p>
<ul>
<li><em>直接使用第三方服务</em></li>
</ul>
<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">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"TheNG"</span><span class="p">,</span> <span class="p">[])</span>
</span><span class='line'> <span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">"AController"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$scope</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$scope</span><span class="p">.</span><span class="nx">someAction</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// handle some logic here</span>
</span><span class='line'> <span class="nx">serviceLoadedFromExternal</span><span class="p">.</span><span class="nx">makeServiceCall</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>
<ul>
<li><em>使用封装成service的第三方服务</em></li>
</ul>
<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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"TheNG"</span><span class="p">,</span> <span class="p">[])</span>
</span><span class='line'> <span class="p">.</span><span class="nx">factory</span><span class="p">(</span><span class="s2">"wrappedService"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">makeServiceCall</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">serviceLoadedFromExternal</span><span class="p">.</span><span class="nx">makeServiceCall</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 class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">"AController"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">wrappedService</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$scope</span><span class="p">.</span><span class="nx">someAction</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// handle some logic here</span>
</span><span class='line'> <span class="nx">wrappedService</span><span class="p">.</span><span class="nx">makeServiceCall</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>
<p>Angular是高度模块化的, 它希望通过这种模块的形式来解决JS代码管理上的混乱, 并且使用依赖注入来自动装配, 这一点与Spring IOC很像, 带来的好处就是你的依赖是可以随意替换的, 这就极大的增加了代码的可测试性.</p>
<ul>
<li><strong>尽量将Ajax请求放到service中去做</strong></li>
</ul>
<p>Angular中使用service来组织那些可被复用的逻辑, 除此之外, 我们也可以将service理解为是对应一个领域对象的操作的集合, 因此, 通常会将一组Ajax操作放在一个service中去统一管理.</p>
<p>当然了,你也可以通过向你的directive或是controller中注入<code>$http</code>, 但是我个人不喜欢这种做法. 首先, <code>$http</code>是一个比较初级的依赖, 与其实注入的业务服务不是一个抽象层级, 如果在你的业务代码中直接操作http请求, 给人的一种感觉就像是在Spring MVC的request method中直接使用<code>HttpServeletRequest</code>一样, 有点突兀, 另外会让整个方法失衡, 因为这些操作的抽象层次是不一样的. 其次就是给测试带来的麻烦, 我们不得不使用<code>$httpBackend</code>来模拟一个HTTP请求的发送.</p>
<p>我们应该设法让测试更简单,通过将Ajax请求封装到service中, 我们只需要让被mock的service返回我们期望的结果就可以了. 只有这样大家才会喜欢写测试, 甚至是做到测试驱动开发, 要去mock<code>$http</code>这样的东西, 显然是增加了测试的负担.</p>
<ul>
<li><strong>使用Promise处理Ajax的返回值, 而不是传递回调函数</strong></li>
</ul>
<p>Angular中所有的Ajax请求默认都返回一个Promise对象, 不建议将处理Ajax返回值的逻辑通过回调函数的形式传递给发送http请求的service, 而应该是在调用service的地方利用返回的promise对象来决定如何处理.</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>
<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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"TheNG"</span><span class="p">,</span> <span class="p">[])</span>
</span><span class='line'> <span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="s2">"deliveryService"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$http</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">validateAddress</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">address</span><span class="p">,</span> <span class="nx">success</span><span class="p">,</span> <span class="nx">failure</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$http</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/unknown'</span><span class="p">,</span> <span class="nx">address</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">success</span><span class="p">,</span> <span class="nx">failure</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 class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">"DeliveryController"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">deliveryService</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">acceptAddress</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="s1">'address accepted'</span><span class="p">);</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">rejectAddress</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="s1">'address rejected'</span><span class="p">);</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'> <span class="nx">$scope</span><span class="p">.</span><span class="nx">validateAddress</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">deliveryService</span><span class="p">.</span><span class="nx">validateAddress</span><span class="p">(</span><span class="nx">$scope</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">acceptAddress</span><span class="p">,</span> <span class="nx">rejectAddress</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>
<p>这里的处理办法是将快递地址验证失败或成功之后的处理函数都传给了<code>deliveryService</code>, 当验证结果从服务器端返回之后, 相应的处理函数会被执行. 这做写法其实是比较常见的, 但是问题出在哪里呢?</p>
<p>其实, 作为一个service的接口, <code>validateAddress</code>应该只接收一个待验证的地址, 验证完成之后返回一个验证结果就可以了, 本来应该是一个很干净的接口, 我们之所以丑陋把对应的处理函数也传进去, 原因就在于这是一个异步的请求, 所以需要在发请求的时候就将对处理函数<em><code>绑定</code></em>上去.</p>
<p>你应该已经猜到了第二个问题我会说一说对它的测试, 通常来说, 如果一个service创建成本较高或是存在外部依赖/请求的话, 我们会将这个service mock掉, 通过让mocked service直接返回我们想要的结果来让我们只关注被验证的业务逻辑.我们回头看一下上面的实现, 如果我们把<code>deliveryService</code>的<code>validateAddress()</code>方法mock掉, 那么我们根本没有办法去验证<code>acceptAddress()</code>和<code>rejectAddress()</code>里面的逻辑!</p>
<p>所以, 如果你的处理函数是传递给service中的API的话, 那么你的测试其实就已经跟这个API的实现绑定了, 你只有去创建一个真实的service并且让它发送HTTP请求, 你的处理函数才会被执行到.</p>
<p>经过这一番折腾, 你一定要说, 这测试比实现代码难写多了. 正确的打开方式应该是这样的: service的API只需要返回promise, 对应的处理函数的<em><code>绑定</code></em>在这个返回的promise上, 这样我们只需要mock那个service的接口让它返回一个我们期望的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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"TheNG"</span><span class="p">,</span> <span class="p">[])</span>
</span><span class='line'> <span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="s2">"deliveryService"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$http</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">validateAddress</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">address</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">$http</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/unknown'</span><span class="p">,</span> <span class="nx">address</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 class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">"DeliveryController"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">deliveryService</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">acceptAddress</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="s1">'address accepted'</span><span class="p">);</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">rejectAddress</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="s1">'address rejected'</span><span class="p">);</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'> <span class="nx">$scope</span><span class="p">.</span><span class="nx">validateAddress</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">deliveryService</span><span class="p">.</span><span class="nx">validateAddress</span><span class="p">(</span><span class="nx">$scope</span><span class="p">.</span><span class="nx">address</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">acceptAddress</span><span class="p">,</span> <span class="nx">rejectAddress</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>
<hr />
<p>本来打算接下来介绍一下Angular代码的单元测试的各种模式的, 写着写着篇幅有点多了, 期待下一篇吧.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Call vs Apply vs Bind]]></title>
<link href="http://fedcuit.github.com/blog/2015/06/10/bind-apply-call/"/>
<updated>2015-06-10T11:29:00+08:00</updated>
<id>http://fedcuit.github.com/blog/2015/06/10/bind-apply-call</id>
<content type="html"><![CDATA[<p>在JavaScript中, 函数是一等公民, 可以作为参数传入另一个函数, 也可以作为返回值从另一个函数中返回, 不仅如此, 在JavaScript这门动态语言中, 函数可以被任意对象调用, 比如说<code>function1</code>是定义在<code>obj1</code>上的, 但是它还可以被<code>obj2</code>所调用, 怎么做到的? 有下面的三种办法:</p>
<ul>
<li><strong>Call</strong></li>
</ul>
<p>基本语法: <code>fnc.call(thisArg, arg1, arg2... argN)</code></p>
<ul>
<li><code>thisArg</code>将作为<code>this</code>对象传入到函数<code>fnc</code>中</li>
<li><code>arg1, arg2... argN</code>作为参数列表传入到函数中</li>
</ul>
<p>通过<code>call</code>方法可以让一个函数被任何的对象所使用, <code>call()</code>会立即返回使用<code>thisArg</code>作为<code>this</code>对象, 余下部分作为参数列表来调用原函数<code>fnc</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">toString</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">delimiter</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"height="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">delimiter</span> <span class="o">+</span> <span class="s2">" weight="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">weight</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">person</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"180"</span><span class="p">;</span>
</span><span class='line'><span class="nx">person</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"80"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s2">","</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=180, weight=80"</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">dog</span> <span class="o">=</span> <span class="p">{};</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"40"</span><span class="p">;</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"30"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">dog</span><span class="p">,</span> <span class="s2">":"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=40: weight=30"</span>
</span></code></pre></td></tr></table></div></figure>
<ul>
<li><strong>Apply</strong></li>
</ul>
<p>上面的例子里<code>toString</code>只是返回了身高与体重的数值, 但是只有数值是没有意义的, 我们还需要单位, 出于演示的目的我们打算将单位传为参数传入<code>toString</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">toString</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">delimiter</span><span class="p">,</span> <span class="nx">heightUnit</span><span class="p">,</span> <span class="nx">weightUnit</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"height="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">heightUnit</span> <span class="o">+</span> <span class="nx">delimiter</span> <span class="o">+</span> <span class="s2">" weight="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">weight</span> <span class="o">+</span> <span class="nx">weightUnit</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">person</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"180"</span><span class="p">;</span>
</span><span class='line'><span class="nx">person</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"80"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s2">","</span><span class="p">,</span> <span class="s2">"cm"</span><span class="p">,</span> <span class="s2">"kg"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=180cm, weight=80kg"</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">dog</span> <span class="o">=</span> <span class="p">{};</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"40"</span><span class="p">;</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"30"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">dog</span><span class="p">,</span> <span class="s2">":"</span><span class="p">,</span> <span class="s2">"cm"</span><span class="p">,</span> <span class="s2">"kg"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=40cm: weight=30kg"</span>
</span></code></pre></td></tr></table></div></figure>
<p>现在在调用<code>call</code>的时候需要传入三个参数(除去thisArg不算), 还不错, 不过接下来我们打算将更进一步, 我们希望在dog对象上添加一个方法<code>justDoIt</code>, 不管这个<code>justDoIt</code>接收什么参数, 都要将对<code>justDoIt</code>函数的调用代理到<code>toString</code>方法上去, 先试试看<code>call</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='javascript'><span class='line'><span class="kd">var</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">toString</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">delimiter</span><span class="p">,</span> <span class="nx">heightUnit</span><span class="p">,</span> <span class="nx">weightUnit</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"height="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">heightUnit</span> <span class="o">+</span> <span class="nx">delimiter</span> <span class="o">+</span> <span class="s2">" weight="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">weight</span> <span class="o">+</span> <span class="nx">weightUnit</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">person</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"180"</span><span class="p">;</span>
</span><span class='line'><span class="nx">person</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"80"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s2">","</span><span class="p">,</span> <span class="s2">"cm"</span><span class="p">,</span> <span class="s2">"kg"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=180cm, weight=80kg"</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">dog</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">justDoIt</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</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 class="p">};</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"40"</span><span class="p">;</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"30"</span><span class="p">;</span>
</span><span class='line'>
</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">dog</span><span class="p">.</span><span class="nx">justDoIt</span><span class="p">(</span><span class="s2">":"</span><span class="p">,</span> <span class="s2">"cm"</span><span class="p">,</span> <span class="s2">"kg"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=40undefined[object Arguments] weight=30undefined"</span>
</span></code></pre></td></tr></table></div></figure>
<p>可以看到<code>arguments</code>这个数组被赋值给了第一个参数<code>delimiter</code>, 而<code>heightUnit</code>, <code>weightUnit</code>都没有被正确的赋值, 所以他们都是<code>undefined</code>.</p>
<p>要解决上面的问题, 就需要<code>apply</code>出场了, <code>apply</code>与<code>call</code>非常的相似, 唯一的区别就是<code>call</code>需要明确地为每一个参数传值, 而<code>apply</code>只需要一个参数数组就可以了, 当原函数被调用的时候, <code>apply</code>会将这个参数数组<code>unpack</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='javascript'><span class='line'><span class="kd">var</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">toString</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">delimiter</span><span class="p">,</span> <span class="nx">heightUnit</span><span class="p">,</span> <span class="nx">weightUnit</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"height="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">heightUnit</span> <span class="o">+</span> <span class="nx">delimiter</span> <span class="o">+</span> <span class="s2">" weight="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">weight</span> <span class="o">+</span> <span class="nx">weightUnit</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">person</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"180"</span><span class="p">;</span>
</span><span class='line'><span class="nx">person</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"80"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s2">","</span><span class="p">,</span> <span class="s2">"cm"</span><span class="p">,</span> <span class="s2">"kg"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=180cm, weight=80kg"</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">dog</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">justDoIt</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">return</span> <span class="nx">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</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 class="p">};</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"40"</span><span class="p">;</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"30"</span><span class="p">;</span>
</span><span class='line'>
</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">dog</span><span class="p">.</span><span class="nx">justDoIt</span><span class="p">(</span><span class="s2">":"</span><span class="p">,</span> <span class="s2">"cm"</span><span class="p">,</span> <span class="s2">"kg"</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=40cm: weight=30kg"</span>
</span></code></pre></td></tr></table></div></figure>
<ul>
<li><strong>Bind</strong></li>
</ul>
<p>如果你对函数式编程有所了解, 那么你应该知道<code>柯里化(currying function)</code>与<code>偏函数(partial function)</code>, 它们有个共同之处就是都是从一个函数上产生出的另一个新的函数.</p>
<p>偏函数可以视为是针对某种情况简化之后的函数, 比如说现在有一个函数<code>awesomeFunction(arg1, arg2, arg3)</code>, 而对这个函数的调用过程中, 第一个参数几乎都是相同的, 作为眼睛里容不得重复的程序员, 我们应该有一种办法可以消除这种重复, 这个时候偏函数就伴着掌声入场了, 我们在偏函数的声明中已经预置了第一个参数<code>arg1=mostFrequentlyUsedValue</code>, 这样我们调用偏参数的时候只需要传入<code>(arg2, arg3</code>)就可以了.</p>
<p>那么现在问题来了, JavaScript中怎么实现偏函数呢?</p>
<p>这时候就需要使用<code>bind</code>了, <code>bind</code>的基本语法与<code>call</code>一致:
<code>fnc.bind(thisArg, arg1, arg2... argN)</code></p>
<p>但是它们有一个重要的区别, 那就是<code>bind</code>不会立即执行原函数, 而是返回一个新的函数, 并且这个新的函数在被调用的时候会自动将已经<code>bind</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">greet</span><span class="p">(</span><span class="nx">timeFrame</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"Good "</span> <span class="o">+</span> <span class="nx">timeFrame</span> <span class="o">+</span> <span class="s2">", "</span> <span class="o">+</span> <span class="nx">msg</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</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">greet</span><span class="p">(</span><span class="s2">"Morning"</span><span class="p">,</span> <span class="s2">"What can I do for you?"</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">greet</span><span class="p">(</span><span class="s2">"Morning"</span><span class="p">,</span> <span class="s2">"This is your ticket"</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">greet</span><span class="p">(</span><span class="s2">"Morning"</span><span class="p">,</span> <span class="s2">"welcome back"</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">greet</span><span class="p">(</span><span class="s2">"Morning"</span><span class="p">,</span> <span class="s2">"How was the trip"</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">greet</span><span class="p">(</span><span class="s2">"Afternoon"</span><span class="p">,</span> <span class="s2">"Can I borrow you some time?"</span><span class="p">));</span>
</span><span class='line'><span class="c1">// "Good Morning, What can I do for you?"</span>
</span><span class='line'><span class="c1">// "Good Morning, This is your ticket"</span>
</span><span class='line'><span class="c1">// "Good Morning, welcome back"</span>
</span><span class='line'><span class="c1">// "Good Morning, How was the trip"</span>
</span><span class='line'><span class="c1">// "Good Afternoon, Can I borrow you some time?"</span>
</span></code></pre></td></tr></table></div></figure>
<p>上面的例子中<code>greet</code>这个函数多数情况下都是使用”Morning”作为第一个参数, 何不创建一个更简短的函数来专门处理这种场景呢:</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='javascript'><span class='line'><span class="kd">function</span> <span class="nx">greet</span><span class="p">(</span><span class="nx">timeFrame</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"Good "</span> <span class="o">+</span> <span class="nx">timeFrame</span> <span class="o">+</span> <span class="s2">", "</span> <span class="o">+</span> <span class="nx">msg</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">morningGreet</span> <span class="o">=</span> <span class="nx">greet</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kc">undefined</span><span class="p">,</span> <span class="s2">"Morning"</span><span class="p">);</span>
</span><span class='line'>
</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">morningGreet</span><span class="p">(</span><span class="s2">"What can I do for you?"</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">morningGreet</span><span class="p">(</span><span class="s2">"This is your ticket"</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">morningGreet</span><span class="p">(</span><span class="s2">"welcome back"</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">morningGreet</span><span class="p">(</span><span class="s2">"How was the trip"</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">greet</span><span class="p">(</span><span class="s2">"Afternoon"</span><span class="p">,</span> <span class="s2">"Can I borrow you some time?"</span><span class="p">));</span>
</span></code></pre></td></tr></table></div></figure>
<p>了解了<code>bind</code>其实是返回一个新的函数之后, 那么其实想通过它来实现上面<code>call</code>/<code>apply</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">toString</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">delimiter</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="s2">"height="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">delimiter</span> <span class="o">+</span> <span class="s2">" weight="</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">weight</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">person</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"180"</span><span class="p">;</span>
</span><span class='line'><span class="nx">person</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"80"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s2">","</span><span class="p">));</span>
</span><span class='line'><span class="c1">//"height=180, weight=80"</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">dog</span> <span class="o">=</span> <span class="p">{};</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="s2">"40"</span><span class="p">;</span>
</span><span class='line'><span class="nx">dog</span><span class="p">.</span><span class="nx">weight</span> <span class="o">=</span> <span class="s2">"30"</span><span class="p">;</span>
</span><span class='line'>
</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">person</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">dog</span><span class="p">,</span> <span class="s2">":"</span><span class="p">)());</span>
</span><span class='line'><span class="c1">//"height=40: weight=30"</span>
</span></code></pre></td></tr></table></div></figure>
<hr />
<p>上面只是从函数可以被任意对象调用的角度来分析了<code>call</code>, <code>apply</code>, <code>bind</code>的用法, 其实实际使用的过程中, 这些方法还有另一个非常重要的用途, 那就是给回调函数绑定正确的上下文环境(this对象).</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Backbone体验 -- View篇]]></title>
<link href="http://fedcuit.github.com/blog/2015/06/04/backbone-view/"/>
<updated>2015-06-04T12:04:00+08:00</updated>
<id>http://fedcuit.github.com/blog/2015/06/04/backbone-view</id>
<content type="html"><![CDATA[<h2>View</h2>
<p>简单讲, <code>View</code>就是用来展示<code>Model</code>里面的数据的.</p>
<p>这样可以简单回顾一下我用过的一些前端JS框架是怎么处理数据展示的, 从最原始的jQuery开始:
只使用jQuery的时候, 当我们已经通过AJAX拿到数据之后, 我们可能需要先定位到需要显示数据的元素然后通过<code>el.val('...')</code>或者<code>el.text('...')</code>将数据手动设置上去. 当元素的值被更新之后需要将修改反应到后端时, 我们可能需要取到所有的元素的最新值然后发送到后台.</p>
<p>如上面那样通过操作对应的单个的DOM元素去设置值的方式写起来很繁琐, 如果返回的数据里面有十几甚至几十个条目的话, 写出来的代码更是毫无美感可言, 于是人们想到了使用JS<code>模板</code>, 通过一段通常是写在<code><head></head></code>中<code><script type="text/template" id="myTemplate">...</script></code>来表示最终要后面的HTML的结构, 在其中使用占位符来标识哪里部分需要被真正传入的数据替换. 模板定义好之后, 当拿到AJAX的返回结果时, 我们只需要将数据填充到这个模板, 然后将生成的HTML添加到页面中就可以了, 一句话就可以搞定, 比如 <code>$container.html(_.template($('#myTemplate'), ajaxReponseData))</code>. 比较常用的模板引擎有<code>mustache</code>, <code>underscore template</code></p>
<p>事情发展到这里还没完, 现在数据显示的问题解决的差不多了, 但是如果用户将某些DOM元素的值修改之后, 我们还需要将所有的元素的值读取一遍, 然后将这些值发送到后台去更新这条记录. 我们通过模板技术解决了繁琐的DOM设值, 但是数据的更新还是一个问题.</p>
<p>一起来看看Backbone的<code>View</code>是如何解决这个问题.</p>
<p>首先我们需要定义一个Backbone <code>View</code>, 定义的方法与<code>Model</code>非常的类似, 只需要继承’View`就可以了:</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>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">TodoItemView</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
</span><span class='line'> <span class="nx">template</span><span class="o">:</span> <span class="nx">_</span><span class="p">.</span><span class="nx">template</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s2">"#todoItemTemplate"</span><span class="p">).</span><span class="nx">html</span><span class="p">()),</span>
</span><span class='line'> <span class="nx">initialize</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">this</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="nx">render</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">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$el</span><span class="p">.</span><span class="nx">html</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">template</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">attributes</span><span class="p">));</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="nx">events</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">"click .toggle"</span><span class="o">:</span> <span class="s2">"handleToggle"</span><span class="p">,</span>
</span><span class='line'> <span class="s2">"click .destroy"</span><span class="o">:</span> <span class="s2">"handleDestroy"</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="nx">handleToggle</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">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">'completed'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">$</span><span class="p">(</span><span class="s1">'.toggle'</span><span class="p">).</span><span class="nx">prop</span><span class="p">(</span><span class="s1">'checked'</span><span class="p">));</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>