-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
524 lines (279 loc) · 615 KB
/
atom.xml
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Mr.喵的网络日志</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2022-08-30T07:58:54.079Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>ValenZhou</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>养犬指南</title>
<link href="http://yoursite.com/2022/08/30/%E5%85%BB%E7%8A%AC%E6%8C%87%E5%8D%97/"/>
<id>http://yoursite.com/2022/08/30/养犬指南/</id>
<published>2022-08-30T07:56:46.000Z</published>
<updated>2022-08-30T07:58:54.079Z</updated>
<content type="html"><![CDATA[<h1 id="养犬笔记"><a href="#养犬笔记" class="headerlink" title="养犬笔记"></a>养犬笔记</h1><h3 id="喂食"><a href="#喂食" class="headerlink" title="喂食"></a>喂食</h3><ol><li><p>一只吃湿的狗粮的狗狗,每天所需要的平均水量是每公斤体重8毫升(即一只10公斤重的狗狗每天需要80毫升水);一只吃干燥狗粮的狗狗每天所需要的平均水量是每公斤体重45毫升(即10公斤重的的狗狗每天需要450毫升水),如果它每天的活动量大或者周围环境的温度高,那么它就需要更多的水。</p></li><li><p>狗狗突然增加饮水量,有可能是生病的征兆。</p></li><li><p>狗狗可以吃牛奶和鸡蛋吗?每个个体的情况是不一样的,许多狗狗不能喝牛奶,或者只能接受一点点,原因是它们对乳糖不耐受。不含乳糖的牛奶大多数狗狗都是可以接受的。生鸡蛋被认为是保持漂亮的皮毛的偏方,但是蛋清含有抗生物素蛋白。如果生吃鸡蛋,会妨碍维生素B族中生物素的吸收,这会对皮肤和皮毛有消极的影响,把鸡蛋煮熟再吃则不会有影响。</p></li><li><p>狗粮</p><ul><li>干型狗粮.这种狗粮是经过高压、脱水、膨化或者挤压、烘干制成的。经过高温后,一些营养成分,例如维生素,已经流失了.干型狗粮在食用之前可以先用水浸湿,这种做法尤其推荐给那些家里有小狗的养狗人。但是狗狗必须多喝水,可接受性比湿型狗粮或者自己做的狗粮要低一些。干型狗粮大多含有大量的碳水化合物。</li><li>半干型狗粮: 含水量是15%~20%。这种狗粮为了防止产生霉菌和细菌,大多含有防腐剂和添加剂。通常半干型的狗粮还会含糖。</li><li>湿型狗粮:通常被存放在罐头或袋子中,含有70%以上的水分。取出狗粮要进行加热、消毒再放到狗狗的食盆中。这个过程中流失的营养成分(例如维生素)需要进行额外补充。</li></ul></li><li><p>在注意产品的外包装上这个产品是主食、补充食品还是辅食(例如零食),另外还有配料、营养成分、添加剂、生产日期、保质期、净含量、湿度(如果高于14%)以及制造商的名字和地址.人造的防腐剂有BHA/E320和BHT/E321。某些人工合成的防腐剂会导致假性过敏或加重过敏症,而且有引起其他疾病的嫌疑。如果您家的狗狗容易过敏,那么您在购买狗粮的时候一定要注意这些添加剂.</p></li><li><p>生肉中含有沙门氏菌。不能喂给狗狗吃。</p></li><li><p>零食不能确保让狗狗喜欢您,但是这种偶尔的小惊喜可以给狗狗带来快乐,甚至可以在学习方面帮助它们。训练零食.这些零食是作为奖励给狗狗的。每一份零食的量应该都很小,因为在训练的过程中会使用很多份零食。</p></li><li><p>您的狗狗已经在期待饱餐一顿了。为了不让它妨碍您为它做饭或者上菜,它应该乖乖坐在一边,或者趴在它的小篮子里,直到您给它信号,告诉它可以开饭了。请您注意,在狗狗吃饭的时候,为它提供一个安静的角落,不让别人打扰它。狗狗的主人有责任维持狗狗用餐环境的卫生。</p></li><li><p>请不要把刚从冰箱里拿出来的食物喂给狗狗吃,这样会引起消化问题和胃病,室内温度也同样重要。在冰箱里冷藏或者冷冻的食物应该提前拿出来解冻。</p></li><li><p>不能给狗狗吃的东西</p><ul><li>巧克力:可可含有对狗狗来说有毒的成分可可碱。</li><li>糖果只给狗狗提供一些没有价值的热量,不应该写进狗狗的饮食计划。</li><li>通常狗狗是不能消化猫粮的。</li><li>生猪肉.</li></ul></li><li><p>狗狗吃饭喜欢狼吞虎咽。它们吃饭太着急了,以至于吃一顿饭和狂欢节一样,让它们用有槽的食盆可以减慢它们吃饭的速度。</p></li><li><p>狗狗的餐具也要保持卫生。水盆一天至少清洗一次,食盆在每次吃饭后都要清洗,最好用水和清洁剂,要彻底冲洗掉清洁剂的残留。</p></li><li><p>嘈杂会对狗狗适应新生活产生负面的影响,尤其是小狗,很容易就感到压力大了。请您多给它一些时间去认识新的家人和新的生活环境。大约一周之后,它渐渐地就可以开始会见拜访者了。如果来到您家里的是一只小狗崽,您最好是坐到地上跟它交流,这样它可以更好地和您取得联系:从一开始您就是它安全的港湾,它可以随时靠岸。在接下来的几天中,也要一步一步地来,告诉它其他可以停留的地方。当狗狗放松下来,您就可以给它喂食了。</p></li></ol><h3 id="日常护理"><a href="#日常护理" class="headerlink" title="日常护理"></a>日常护理</h3><ol><li><p>刷子、梳子还有其他身体护理工具给您提供全心全意为狗狗服务的机会。请您抛开每天的忙碌和压力,抽出时间来做这件事。您和狗狗可以享受这段二人世界的美好时光,关系也能变得更亲密。因为互相护理身体不仅在狗狗之间可以起到增进友谊的作用,也适用于狗狗和人之间的关系。</p></li><li><p>狗狗沐浴露.只能使用那种可以保湿、含有油脂的狗狗专用沐浴露。人类使用的产品会损害狗狗皮肤上的酸性保护层。</p></li><li><p>耳部护理乳液.用来清洁和护理耳朵的乳液。</p></li><li><p>泡澡.<br> 狗狗一年泡2~3次澡就够了。如果这个调皮鬼在粪便里面打滚了,那就要额外再泡一次澡了。为除去皮毛上的尘土和脏东西,用淋浴冲一下就行了,不需要用沐浴露。在泡澡之前,请您给狗狗用刷子刷一下皮毛,在浴盆下面铺一层防滑的垫子。如果您家的狗狗不喜欢泡澡,那么给它泡澡的时候应该有一个人帮您抓着它。先用温水给它冲一遍身体,然后抹上沐浴露。不要让水进入它的耳朵和眼睛里。用水冲掉泡沫,然后再抹一次沐浴露。最后用清水冲洗干净。洗完后快速用毛巾把狗狗包裹起来,否则它会抖动身体想要甩掉身上的水。请您仔细给它擦干净身上的水。如果外面比较温暖,也可以自然风干。如果外面温度较低或狗狗的毛比较长,那就需要用到吹风机了,这样狗狗就不会着凉了。只有当它的身体完全干了以后才可以到外面比较冷的地方去。</p></li><li><p>跳蚤.<br> 跳蚤会一整年都困扰着狗狗,尤其是当天气温暖湿润的时候,它们就会大量繁殖。强烈的瘙痒大多是狗狗被跳蚤袭击的证据。一般来说,当您用一种齿特别细密的梳子给狗狗梳理毛发的时候就能找到跳蚤了。其他征兆还有狗狗皮肤上红色的小斑点或者一些微小的深褐色的跳蚤粪便。跳蚤的卵、幼虫和蛹生活在地毯和家具之类的环境中。如果不把这些东西消灭干净,烦恼永远不会消失。因此,仅仅是处理狗狗身上的跳蚤是不够的,周围的环境也要大扫除。可以在动物医生那里找到去除跳蚤的药物。</p></li><li><p>壁虱<br> 壁虱会隐藏在草丛里或者其他低矮、靠近地面的植被中,例如草地等.在动物专用品商店和动物医生那里都有专门去除壁虱的工具。请您按照产品说明进行操作,就可以顺利去除壁虱了。然后在被壁虱咬伤的伤口涂上消毒的药物。不要把您抓到的壁虱捏碎,避免更多的壁虱的唾液从伤口进入身体。此外,您应该在每一次外出散步回来的时候检查一下狗狗的身体,把狗毛里的壁虱找出来.</p></li><li><p>眼部清洁:用湿润的化妆棉轻敷狗狗眼角的分泌物形成的痂,然后把它们擦掉.</p></li><li><p>医学训练.<br> 狗狗首先要学的是,让主人给它梳理毛发、刷牙、检查耳朵、固定住整个身体。请您从一开始就用游戏的方式训练狗狗适应这些身体护理的方式。可以使用自然材料制成的刷子练习给狗狗梳理皮毛。您和狗狗依偎在一起的时候,可以顺便触摸它的身体,给它检查耳朵等,然后因为它的配合而表扬它或者给它一些好吃的零食,这样狗狗甚至会爱上这种身体护理的方式。</p></li><li><p>如果指甲太长了,必须使用特制的指甲剪给它剪指甲。初学者要向动物医生或者狗狗饲养者学习这种技术。不要剪得太短,误伤会非常疼,有可能导致狗狗以后一看到指甲剪就陷入恐慌。</p></li></ol><p>10.预防措施: </p><pre><code>- 均衡的膳食可以帮助狗狗保持健康,- 每年至少要带狗狗去看一次动物医生,让医生给它做身体检查- 适量的活动对于每一只狗狗来说都是必需的。散步就足够了.- 那些让狗狗感觉不能承受的压力,会让它们生病,这些压力可能由多种因素引起,例如活动太少或者太多、让狗狗做人才能做的事、忽视它们、错误的教育或者缺少教育、错误的饲养以及反复无常的行为。- 充分的疫苗接种可以避免狗狗患上那些危险的传染病。除虫和预防寄生虫的措施可以防止狗狗的健康受到损害。</code></pre><h3 id="正确的教育狗狗"><a href="#正确的教育狗狗" class="headerlink" title="正确的教育狗狗"></a>正确的教育狗狗</h3><ol><li><p>那些清楚地知道自己可以做什么的狗狗,在这个框架中可以不受约束地自由行动。给狗狗划定界限并不意味着限制它,而是给它指引方向。一只狗狗,如果主人一召唤它回来,它就很听话地回来了,那么,主人就不会给它拴遛狗绳,而是让它自由地去追逐嬉戏。如果它不知道界限在哪里,它必然就会越线,表现出一些您不喜欢甚至导致严重的问题的行为。</p></li><li><p>具有可预见性的行为,也属于清晰框架的一部分。如果以前不允许狗狗做的事现在又允许它做了,三天之后又不允许它做了,这可能是您和狗狗关系的头号杀手。</p></li><li><p>您可以有多种途径和狗狗进行沟通。</p><ul><li>声音信号.和狗狗的有意识的交流大多数时候是通过声音信号。请您给您的狗狗一个声音信号,例如“坐下”,在您说出指令的时候教给它如何做,然后它听到这个指令就知道您想让它干什么了。视觉信号<br> - 视觉信号也是一样的。只不过这些是可以看见的信号,例如举起食指代表让它坐下。<br>声音信号和视觉信号的原理非常简单,但交流是件复杂的事,您传达给狗狗的信号远远比您想象的要多。</li></ul></li><li><p>您希望您的狗狗正确理解您的意思,不出现误解,那么您在和它交流的时候就要简单明确,避免误解。</p></li><li><p>如果您想爱抚它,可以从下面抚摸它的胸部,不要摸它的头。从上方“友好地”摸它的头,会让很多狗狗觉得受到了威胁。</p></li><li><p>靠狗狗太近,甚至在它的上方弯腰或者直接看着它的眼睛,会让它感觉到自己受到了威胁,甚至有可能让它觉得您在挑衅它。</p></li><li><p>如果您不想让狗狗感到不安,那么就尽量避免出现上述情况。例如,当您想要给它系上遛狗绳的时候,您会在它的上方弯腰,这时它会后退,蜷缩起身体,甚至反复地吐舌头,因为它感觉到不舒服。这个时候您最好蹲下。如果有必要,您可以有目的地使用一些威胁性的行为,告诉您的狗狗:“现在不是玩闹的时候了!”有些狗狗,只需严厉地看着它,就可以给它留下一个深刻的印象了;另外一些狗狗,则需要做更多表示。注意,这些都有可能导致某些狗狗出现一些激烈甚至是危险的反应,只有在您能保证安全的情况下才能使用。</p></li><li><p>听到您的命令以后坐到地上,对于狗狗来说是最简单的练习之一。狗狗学习的速度,尤其是学习复杂事情的速度,是不同的,有些需要多一些时间,有些则相当迅速。您会不停地给它一些信号,而且不仅仅是那些您已经教给它的声音和视觉信号。您想让它在您吃饭的时候不要在餐桌边吠叫,就不要从餐桌上拿任何食物给它。任何您分享给它的食物或者不小心从餐桌上掉下来的食物都会让它觉得,耐心等待就会有回报,它只要坚持不懈地守在餐桌旁就一定会有好吃的。粗心马虎有可能导致狗狗出现不良行为,因此请您注意,不要让狗狗学到一些不该学的东西。</p></li><li><p>通过联系进行学习.联系的意思是把一种刺激和一种行动联系起来,这两者被放到一起就是经典条件反射(巴甫洛夫的狗)。您拿起遛狗绳,狗狗就知道马上要去散步了。您把它的牙刷拿在手上,它就知道您是想给它刷牙了。</p></li><li><p>通过发出声音信号或者视觉信号来学习,这意味着要把某种信号和一种行为联系在一起。<br>例如,您和狗狗一起练习,当您对它说“坐下”或者举起您的食指的时候,让它坐下。如果它能正确做出您想要的行动,它就会得到表扬,甚至会得到好吃的零食作为奖励。您的积极的反应告诉它,它的行为是正确的。假设您的狗狗想要从垃圾桶里掏东西。它成功地掀开了垃圾桶的盖子,但是盖子“啪嗒”一声掉了下来,砸在了它的头上,让它感觉到了疼痛,它就会被吓跑了。它的这种行为会产生一个消极的结果,这种经历将阻止它再去翻垃圾桶。</p></li><li><p>在训练的过程中,狗狗承受的压力很大,而且在做某些练习的时候,它的注意力只能集中几分钟。</p><ul><li>请您在练习的过程中安排一些休息时间。如果狗狗成功地完成了一个练习,那么在这次训练中就不要再重复这个练习了。(过犹不及)</li><li>请您把练习过程分成一段一段小的练习。(循序渐进)</li><li>您的狗狗只有在不被其他事物分散注意力的时候才可以专注到训练当中去。每次开始新的练习的时候,请您把练习地点安排在没有什么能分散它的注意力的地方.(集中注意力)</li><li>狗狗会把学到的东西和某一个地点联系起来。为了让狗狗能够在任何地方都能成功地完成练习,您需要变换不同的地点进行练习。(变换地点)</li><li>有些练习需要一百次甚至更多次重复。(不停地重复)</li></ul></li><li><p>有目的性地进行奖励.<br>只有当狗狗做出了您想要的行为时才能得到奖励。例如,在“坐下”的练习中,不要在狗狗站起身来的时候奖励它。请您在狗狗端正地坐下,触碰到地面的那一刻对它进行奖励。这样,就不会产生误解了.</p></li><li><p>每次训练结束的时候,您可以给出一个结束性的信号,例如“跑”,这可以告诉狗狗,训练结束了。</p></li><li><p>可以惩罚狗狗吗?<br>您的目的是终止您不希望看到的行为,并且在今后尽量避免再出现这样的行为。要达到这个目的,必须使用一些您的狗狗能够理解的方式。但是这些方式不能被认为是惩罚,因为您是在纠正狗狗的错误,而不是想要报复它。纠正错误这个行为必须适度,而且要给它留下深刻的印象。对于某些狗狗来说,一个严厉的眼神、一句敦促它的话或者一声轻咳就足够了。或者,让狗狗知道主人已经做好了采取措施的准备了。如何给狗狗这样一种印象呢?例如,您可以果断地朝它走去,以这种方式来限制它的活动范围,如果有必要的话,可以在它的上方弯下腰来,盯着它看。如果它还是不改变自己的行为,那么就有必要按照狗狗的方式轻度地拍它一下或者碰它一下。在合适的情况下使用这种方法,也是非常有效的。</p></li><li><p>责罚和暴力在对狗狗的教育中毫无价值。而且您要在狗狗犯错的当时给它纠正,不要等事情都过去了才纠正。</p></li><li><p>有意义的教育辅助工具: 零食,哨子,食物包,牵引绳.</p></li><li><p>训练狗狗不随地大小便.方便的地点:请您在狗狗来到您家的第一天就指给它看,它可以在花园里或者您家门前的哪个地点大小便。每次它到固定地点去大小便的时候,您都要开心地表扬它,以此来加深它对这件事的印象。如果您能把狗狗上厕所这件事和一个口令联系起来,那么它以后一听到这个口令就会上厕所。</p></li><li><p>使用响片是一种以积极强化为基础的训练方式。首先要训练狗狗对响片的声音产生条件反射。它要知道,这个声响代表食物。响声要和积极的事物联系起来。如果它做出了您所希望的行为,那么就要伴随着响声,给它食物作为奖励。起关键作用的是响片发出响声的时机。狗狗是通过尝试和犯错来学习的。</p></li><li><p>中断指令: 可以适当和它练习一些基础的中断指令,如: 不行,停下,吐出来等.请您在时机合适的时候练习,并且使用不同的物品,也可以用食物和用来啃咬的骨头,但是练习不要太过频繁。这样,您的狗狗就能学习到:放弃一些东西是有回报的。</p></li><li><p>在狗笼里也可以很快乐.<br>狗笼不是储藏室或者惩罚手段!它应该是狗狗的一个舒适的避风港。当周围环境对于它来说比较危险或者需要休息时,它可以短时间地待在那里。它在那里可以不被任何人打扰。请您在狗笼里铺上柔软温暖的垫子,放些装有饮用水的水盆和玩具。请您让它待在狗笼里,给它喂食,且只有它在那里的时候才喂。做这些的时候要保持狗笼的门打开.如果您的狗狗趴在笼子里啃骨头或者睡着了,您可以把笼子的门先虚掩着,几次之后,可以短暂地锁上。如果小狗没有对您锁门的行为做出反抗,那么您可以让锁门的时间变得越来越长。如果小狗看到您把门锁上就非常绝望地发出叫声,请您先让它慢慢安静下来,然后再把它放出来,否则它就会产生这样的联想:我一发出悲惨的叫声就能被放出去了。<br>请您耐心一些,不久之后您的狗狗肯定就能自己钻进狗笼里去了。</p></li><li><p>您的狗狗必须要学着自己在家,这样它才能放松地等着您回来。<br>请您不要在晚上让小狗独处,这对于它来说会是噩梦般的经历。当狗狗发出诉苦的叫声时,请您不要马上回来,而是要等它安静下来的时候再回来。不能让它产生这样的联想:您是听到了它的诉苦才回来的。在您离开之前,不要对狗狗表示怜悯和同情,这会让它的独处变得更困难。但是您跟它分别的时候要遵循一定的程序。这样,它就可以明白,您还是会回到它的身边的。</p></li></ol><h3 id="常用的训练指令"><a href="#常用的训练指令" class="headerlink" title="常用的训练指令"></a>常用的训练指令</h3><ol><li><p>看这里<br> 如果您的狗狗听到您的这个命令朝您看过来,您就成功了。集中注意力的训练非常简单,而且还很有趣:请您把一块好吃的东西藏在手里,然后把手放到背后。对狗狗说“看这里”,如果您的狗狗向您看过来,那么就在这时迅速将您手中的美食给它。请您经常做这个练习,让它快速熟悉这个口令。</p></li><li><p>到这儿来</p></li><li><p>坐下<br> 请您把一块食物拿在手里,抬起食指,然后把它拿到狗狗的鼻子前面并举过它的头顶。狗狗为了能够继续观察您手中的食物,有可能会坐下。如果它坐下了,您就说“坐下”,同时把食物给它,并且表扬它。在这个练习结束以后,您可以给它一个结束的信号。</p></li><li><p>趴下<br> 请您用大拇指和手掌夹住一块食物。让您的狗狗闻一闻食物,然后慢慢地把手从狗狗的鼻子前面移动到地面上。在这个过程中您的手掌向下翻转。狗狗为了追寻食物,很有可能会趴下。如果狗狗趴下了,您马上要说出“趴下”,把食物给它,对它进行表扬。如果您的狗狗并没有趴下,您可以把食物在地上向着远离它的方向移动。您还可以在椅子下面移动食物。这样,狗狗如果想继续追寻食物,就必须趴下了。练习结束的时候请您发出结束的指令。</p></li><li><p>别动<br> 请您在开始训练前让狗狗先趴下,这样练习对于它来说会容易多。狗狗趴在那儿。然后您发出指令“不要动”,手掌竖直放在它面前,像一个禁止前进的牌子。请您后退一步。如果它还是没有动,您就向它走过去,给它一些食物并且表扬它。如果它成功完成了这个练习,那么您可以扩大距离继续进行练习,每一次成功都要给它奖励和表扬。请您变换方向和距离,绕着它走,继续重复这个练习。之后再练习,您可以在周围比较安全的地方从狗狗的视野中消失一段时间。如果在这个过程中,您的狗狗站起来了,那么请您让它安静下来,重新回到训练的原点,重复短距离或者短时间的趴着不动的练习。最后给狗狗一个结束训练的信号。</p></li></ol><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><ol><li><p>狗狗狂吠、吼叫,拆卸家具或者把家具、地毯、门等挠坏,是压力大的表现。可能的原因是狗狗还没有学习如何独处。分离的恐慌经常是由不安全引起的.在让狗狗独处之前需要进行一个和它分离的仪式,以这种方式使狗狗对所处情境进行评估。</p></li><li><p>攻击性行为的原因多种多样。通常起因是一些资源,例如食物、玩具或者和主人的亲密,或者狗狗的主人太过娇纵它了。攻击性行为通常是狗狗表达不安的方式,狗狗把它视为预防性措施或者最后的出路。狗狗也有可能因为接收到的要求太多或者压力过大而变得具有攻击性,例如它们从主人那里得不到足够的关怀。对狗狗的要求太低、社交太少或者教育缺失,缺乏运动.</p></li><li><p>用水枪或者矿泉水瓶子向狗狗喷水,以此来中断它的行为,让它重新开始接受主人发出的指令。请您不要直接向狗狗的面部喷水,而是向它的耳朵或者脖子喷水。正确的时间点是喷水起作用的关键。</p></li><li><p>玩游戏的规则很简单:不要容忍任何让您感到不舒服或者给您带来疼痛的行为。如果狗狗变得疯狂起来,甚至咬您的衣服、胳膊,请您给它一个中断信号,中场休息一下或者结束游戏。</p></li><li><p>社会性行为和熟悉周边环境——这些基本的经历发生在狗狗出生后最初的几个月中.虽然狗狗在任何年龄阶段都可以学习,但是不会再像这个阶段这样容易了,现在学到的东西一辈子都不会忘记。小狗在这个时候要通过尝试和犯错学习控制在打闹中啃咬的轻重程度。游戏中学习到的东西对于小家伙以后的生活非常重要。此外,游戏还可以促进它的运动机能,增强身体素质。请为您的狗狗提供一些积极的经历,这些会让它终身受用。尽量让它熟悉那些对它今后的生活非常有用的事。</p></li><li><p>在公寓房里生活的狗狗也可以很幸福。前提是选择适合生活在室内的狗狗,除此之外还要有靠谱的主人,为狗狗提供除了散步以外的其他活动来平衡减少的室外活动。您是生活在宫殿里还是一居室里,对于狗狗来说都无所谓,它需要的是获得了身体和精神所需的活动。</p></li><li><p>对于一只狗狗来说,得到宠爱并不意味着主人给它买很多玩具。当它的主人肯抽出时间跟它一起散步、玩耍、工作,让它依偎在自己身边,这时的狗狗才算是幸福的。您能给予狗狗的最宝贵的东西就是您的关心。您的狗狗会因此而感谢您信任您、对您发出的命令无条件服从。</p></li><li><p>不能毫无限制地亲近,您和狗狗身体上的亲密对于它来说是一种资源,您需要和狗狗保持亲密的关系,但是也不能让它每次索要时都能得到。这并不是冷酷无情,而是一种正常的行为。</p></li><li><p>一只心情一般的狗狗,它的身体是放松的:尾巴不紧绷或者微微下垂,耳朵直立。如果有什么事情引起了它的注意,它的身体会紧张起来,尾巴翘起,耳朵略微探向前方。而有所期待的狗狗身体会略显紧张,尾巴水平方向摇晃,嘴巴略微张开,面部表情聚精会神。</p></li><li><p>狗狗在感到害怕或者不安时,它们的眼神是躲闪的,同时表现得非常卑微:低着头,弯曲着腿,尾巴耷拉着或者夹在两条腿中间,耳朵耷拉着。应该避免一切会增加狗狗不安的事情。</p></li><li><p>狗狗在被动地臣服于其他狗狗的时候,会仰面躺下,把自己最脆弱的地方展现给对方。同时,它还会保持等待的姿态,嘴角拉得很长,有时候还会尿尿。</p></li><li><p>主动的屈从也被称为缓和关系。如果狗狗想要缓和它们之间紧张的气氛,它会舔自己的嘴巴和鼻子,或者尝试着去舔对方的嘴角。这种行为来源于它们的孩提时代:通过舔成年狗狗的嘴角,小狗可以得到它们想要的食物。</p></li><li><p>一只想要威胁对方的狗会一直盯着对方,皱起鼻子周围的皮肤,抬起上嘴唇下垂的部分,露出牙齿。根据激烈程度的不同,它背上的毛竖起来的程度也不同。它还会发出呼噜声或者朝对方吠叫。</p></li><li><p>怎样才能小狗习惯它的名字?不要在骂它的时候叫它的名字,只有在给它喂食或者给它玩具这类好事的时候才叫它的名字</p></li><li><p>可以让10周大的小狗长时间单独4待在家吗?不可以,小狗需要学习才能单独待在家。如果现在就延长让它单独在家的时间,有可能会让它产生分离的恐惧。此外,如果让它单独在家,它就会学着在屋里大小便了,这对于训练它们不随地大小便是不利的.</p></li><li><p>怎样正确地抱起狗狗?在抱起小狗的时候必须好好支撑住它。一只手扶着它的胸部,最好是把食指放在两条前腿中间。另外一只手支撑住它的屁股。然后小心翼翼地把它放到您的胸前。一定要把狗狗固定在臂弯里,这样它就不会挣扎着摆脱您的双手,然后掉到地上.</p></li></ol>]]></content>
<summary type="html">
<h1 id="养犬笔记"><a href="#养犬笔记" class="headerlink" title="养犬笔记"></a>养犬笔记</h1><h3 id="喂食"><a href="#喂食" class="headerlink" title="喂食"></a>喂食</h
</summary>
</entry>
<entry>
<title>读《时间简史》</title>
<link href="http://yoursite.com/2022/03/31/%E8%AF%BB%E3%80%8A%E6%97%B6%E9%97%B4%E7%AE%80%E5%8F%B2%E3%80%8B/"/>
<id>http://yoursite.com/2022/03/31/读《时间简史》/</id>
<published>2022-03-31T06:22:01.000Z</published>
<updated>2022-03-31T06:24:14.251Z</updated>
<content type="html"><![CDATA[<h1 id="时间简史"><a href="#时间简史" class="headerlink" title="时间简史"></a>时间简史</h1><blockquote><p>摘录自 史蒂芬霍金 <<时间简史>></p></blockquote><h3 id="我们的宇宙图象"><a href="#我们的宇宙图象" class="headerlink" title="我们的宇宙图象"></a>我们的宇宙图象</h3><p>早在公元前 340 年,希腊哲学家亚里士多德在他的《论天》一书中,就能够对于地球是一个圆球而不是一块平板这个信念提出两个有力的论证。第一,他意识到,月食是由于地球运行到太阳与月亮之间引起的。第二,希腊人从旅行中知道,在南方观测北极星,比在较北地区,北极星在天空中显得较低。希腊人甚至为地球是球形提供了第三个论证,否则何以从地平线驶来的船总是先露出船帆,然后才露出船身?</p><p>亚里士多德认为地球是不动的,太阳、月亮、行星和恒星都以圆周为轨道围绕着地球公转。他相信这些,是因为他认为地球是宇宙的中心,而圆周运动是最完美的;他的这种看法是基于某些神秘的原因。公元 2 世纪,这个思想被托勒密精制成一个完整的宇宙学模型。地球处于正中心,8 个天球包围着它,这 8 个天球分别负载着月亮、太阳、恒星和 5 个当时已知的行星:水星、金星、火星、木星和土星。托勒密模型的系统可以相当精密地预言天体在天空中的位置。但是为了正确地预言这些位置,托勒密不得不假定,月亮遵循的轨道有时使它离地球的距离是其他时候的一半。它被基督教会接纳为与《圣经》相一致的宇宙图象。这是因为它具有巨大优势,即在固定恒星天球之外为天堂和地狱留下了大量的空间。</p><p>然而,1514 年波兰教士尼古拉·哥白尼提出了一个更简单的模型。他的观念是,太阳静止地位于中心,而地球和行星们围绕着太阳做圆周运动。后来,两位天文学家——德国人约翰斯·开普勒和意大利人伽利略·伽利雷开始公开支持哥白尼理论,尽管它所预言的轨道还不能完全与观测相符合。直到 1609 年,亚里士多德和托勒密的理论才宣告死亡。那一年,伽利略用刚发明的望远镜来观测夜空。当他观测木星时,发现有几个小卫星或月亮围绕着它转动,这表明不像亚里士多德和托勒密设想的那样,并非所有东西都必须直接地围绕着地球转动。同时,约翰斯·开普勒修正了哥白尼理论,提出行星不是沿着圆周而是沿着椭圆(椭圆是拉长的圆)运动,从而最终使预言和观察相互一致了。虽然他几乎偶然地发现椭圆轨道能很好地和观测相符合,但却不能把它和他的磁力引起行星围绕太阳运动的思想相互调和起来。</p><p>只有到更晚得多的 1687 年,这一切才得到解释。这一年,艾萨克·牛顿爵士出版了他的《自然哲学的数学原理》,牛顿不但提出物体如何在空间和时间中运动的理论,并且发展了为分析这些运动所需的复杂的数学。此外,牛顿还提出了万有引力定律。根据这条定律,宇宙中的任一物体都被另外的物体吸引。物体质量越大,相互距离越近,则相互之间的吸引力越大。正是这同一种力,使物体下落到地面。牛顿继而证明,根据他的定律,引力使月亮沿着椭圆轨道围绕着地球运行,而地球和其他行星沿着椭圆轨道围绕着太阳公转。</p><p>在 20 世纪之前从未有人提出过,宇宙是在膨胀或是在收缩,这有趣地反映了当时的思维风气。一般认为,宇宙要么以一种不变的状态存在了无限长的时间,要么以多多少少正如我们今天观察到的样子在有限久的过去创生。其部分的原因可能是,人们倾向于相信永恒的真理,也可能由于从以下的观念可以得到安慰,即虽然他们会生老病死,但是宇宙必须是不朽的不变的。</p><p>1781 年,哲学家伊曼努尔·康德发表了里程碑般的(也是非常晦涩难懂的)著作《纯粹理性批判》。在这本书中,他深入地考察了关于宇宙在时间上是否有开端、在空间上是否有限的问题。他称这些问题为纯粹理性的二律背反(也就是矛盾)。因为他感到存在同样令人信服的论据,来证明宇宙有开端的正命题,以及宇宙已经存在无限久的反命题。他对正命题的论证是:如果宇宙没有一个开端,则任何事件之前必有无限的时间。他认为这是荒谬的。他对反命题的论证是:如果宇宙有一开端,在它之前必有无限的时间,为何宇宙必须在某一特定的时刻开始呢?事实上,他对正命题和反命题用同样的论证来辩护。它们都是基于他隐含的假设,即不管宇宙是否存在了无限久。时间均可无限地倒溯回去。我们将会看到,在宇宙开端之前时间概念是没有意义的。</p><p>1929 年,埃德温·哈勃作出了一个里程碑式的观测,即不管你往哪个方向观测,远处的星系都正急速地飞离我们而去。换言之,宇宙正在膨胀。这意味着,在早先的时刻星体更加相互靠近。事实上,似乎在大约 100 亿至 200 亿年之前的某一时刻,它们刚好在同一地方,所以那时候宇宙的密度为无限大。如果在这个时刻之前有过一些事件,它们将不可能影响现在发生的东西。因为它们没有任何观测的后果,所以可不理睬其存在。由于更早的时间根本没有定义,所以在这个意义上,人们可以说,时间在大爆炸时有一开端。</p><h3 id="时间和空间"><a href="#时间和空间" class="headerlink" title="时间和空间"></a>时间和空间</h3><p>我们现在关于物体运动的观念来自于伽利略和牛顿。在他们之前,人们相信亚里士多德,他说物体的自然状态是静止的,并且只有在受到力或冲击的推动时才运动。这样,重的物体比轻的物体下落得更快,因为它受到更大的将其拉向地球的力。在伽利略之前,没有一个人想看看不同重量的物体是否确实以不同速度下落。据说,伽利略从比萨斜塔上将重物落下,从而证明了亚里士多德的信念是错的。这故事几乎不足以信,但是伽利略的确做了一些等效的事:让不同重量的球沿光滑的斜面上滚下。这种情况类似于重物的垂直下落,只是因为速度小而更容易观察而已。伽利略的测量指出,不管物体的重量多少,其速度增加的速率是一样的。一个铅锤比一片羽毛下落得更快些,那只是因为空气阻力将羽毛的速度降低。如果一个人释放两个不受任何空气阻力的物体,例如两个不同的铅锤,它们则以同样速度下降。在没有空气阻碍东西下落的月球上,航天员大卫·斯各特进行了羽毛和铅锤实验,并且发现两者确实同时落到月面上。</p><p>牛顿把伽利略的测量当做他的运动定律的基础。在伽利略的实验中,当物体从斜坡上滚下时,它一直受到不变外力(它的重量)的作用,其效应是使它恒定地加速。这表明,力的真正效应总是改变物体的速度,而不是像原先想象的那样,仅仅使之运动。同时,它还意味着,只要物体没有受到外力,它就会以同样的速度保持直线运动。这个思想首次在牛顿于 1687 年出版的《数学原理》一书中明白地陈述出来了,并被称为牛顿第一定律。牛顿第二定律给出物体在受力时发生的现象:物体在被加速或改变其速度时,其改变率与所受的外力成比例。除了他的运动定律,牛顿还发现了描述引力的定律:任何两个物体都相互吸引,其引力大小与每个物体的质量成比例。于是,如果其中一个物体的质量加倍,则两个物体之间的引力加倍。牛顿引力定律还告诉我们,物体之间的距离越远,则引力越小。</p><p>亚里士多德和伽利略-牛顿观念的巨大差别在于,亚里士多德相信一个优越的静止状态,任何没有受到外力和冲击的物体都取这种状态。特别是他以为地球是静止的但是从牛顿定律可以推断,并不存在唯一的静止标准。例如,在有轨电车上打乒乓球,人们将会发现,正如在铁轨旁一张台桌上的球一样,乒乓球服从牛顿定律,所以无法得知究竟是火车还是地球在运动。</p><p>亚里士多德和牛顿都相信绝对时间。也就是说,他们相信人们可以毫不含糊地测量两个事件之间的时间间隔,只要用好的钟,不管谁去测量,这个时间都是一样的。时间相对于空间是完全分离并且独立的。这就是大部分人当做常识的观点。然而,我们必须改变这种关于空间和时间的观念,虽然这种显而易见的常识可以很好地对付运动甚慢的诸如苹果、行星的问题,但在处理以光速或接近光速运动的物体时却根本无效。</p><p>1865 年,当英国的物理学家詹姆士·克拉克·麦克斯韦成功地将直到当时用以描述电力和磁力的部分理论统一起来以后,才有了光传播的正确理论。麦克斯韦理论预言,射电波或光波应以某一固定的速度行进。但是牛顿理论已经摆脱了绝对静止的观念,所以如果假定光以固定的速度旅行,人们就必须说清这固定的速度是相对于何物来测量的。因此有人提出,存在着一种无所不在的称为“以太”的物质,甚至在“真空的”空间中也是如此。然而,一位迄至当时还默默无闻的瑞士专利局的职员阿尔伯特·爱因斯坦,在 1905 年的一篇狭义相对论中指出,只要人们愿意拋弃绝对时间观念的话,整个以太的观念则是多余的。几个星期之后,法国第一流的数学家亨利·庞加莱也提出类似的观点。爱因斯坦的论证比庞加莱的论证更接近物理,后者将其考虑为数学问题。通常将这个新理论归功于爱因斯坦,但人们不会忘记,庞加莱的名字在其中起了重要的作用。不管观察者运动多快,他们应测量到一样的光速。这简单的观念有一些非凡的结论。可能最著名者莫过于质量和能量的等价,这可用爱因斯坦著名的方程 E=mc2 来表达(E 是能量,m 是质量,c 是光速),以及没有任何东西可能行进得比光还快的定律。当一个物体接近光速时,它的质量上升得越来越快,这样它需要越来越多的能量才能进一步加速上去。实际上它永远不可能达到光速,因为那时质量会变成无限大,而根据质量能量等价原理,这就需要无限大的能量才能做到。由于这个原因,相对论限制了物体运动的速度:任何正常的物体永远以低于光速的速度运动,只有光或其他没有内禀质量的波才能以光速运动。</p><p>相对论终结了绝对时间的观念!每个观察者都可以利用雷达发出光或射电波脉冲来说明一个事件在何处何时发生。一部分脉冲在事件反射回来后,观察者可在他接收到回波时测量时间:事件的时间可认为是脉冲被发出和反射被接收的两个时刻的中点;而事件的距离可取这来回行程时间的一半乘以光速。现在我们正是用这种方法来准确地测量距离,因为我们可以将时间测量得比长度更为准确。实际上,米是被定义为光在以铯原子钟测量的 0.000000003335640952 秒内行进的距离(取这个特别数字的原因是,因为它对应于历史上的米的定义——按照保存在巴黎的特定铂棒上的两个刻度之间的距离)。同样地,我们可以用叫做光秒的更方便的新长度单位,这就是简单地定义为光在 1 秒中行进的距离。</p><p>相对论迫使我们从根本上改变了我们的时间和空间观念。我们必须接受,时间不能完全脱离和独立于空间,而必须和空间结合在一起形成所谓的时空的客体。</p><p>这正如同将一块石头扔到池塘里,水表面的涟漪向四周散开一样,涟漪作为一个圆周散开并随时间越变越大。如果人们把不同时刻涟漪的快照逐个堆叠起来,扩大的水波圆周就会画出一个圆锥,其顶点正是石块击到水面的地方和时刻。类似地,从一个事件散开的光在(四维的)时空里形成了一个(三维的)圆锥,这个圆锥称为事件的将来光锥。以同样的方法可以画出另一个称为过去光锥的圆锥,它表示所有可以用一个光脉冲传播到该事件的事件集合。不处于 P 的将来或过去的事件被称之为处于 P 的他处。在这种事件处所发生的东西既不能影响发生在 P 的事件,也不受发生在 P 的事件的影响。例如,假定太阳就在此刻停止发光,它不会对此刻的地球上的事情发生影响,因为它们是在太阳熄灭这一事件的他处。我们只能在 8 分钟之后才知道这一事件,这是光从太阳到达我们所花费的时间。只有到那时候,地球上的事件才在太阳熄灭这一事件的将来光锥之内。当我们看宇宙时,我们是在看它的过去。</p><p>狭义相对论非常成功地解释了如下事实:对所有观察者而言,光速都是一样的,并成功地描述了当物体以接近于光速运动时会发生什么。然而,它和牛顿引力理论不相协调。牛顿理论说,物体之间相互吸引,其吸引力依赖于它们之间的距离。这意味着,如果我们移动其中一个物体,另一物体所受的力就会立即改变.爱因斯坦在 1908 年至 1914 年之间进行了多次不成功的尝试,企图找到一个和狭义相对论协调的引力理论。1915 年,他终于提出了今天我们称为广义相对论的理论。爱因斯坦提出了革命性的思想,即引力不像其他种类的力,它只不过是时空不是平坦的这一事实的结果,在时空中的质量和能量的分布使它弯曲或“翘曲”。太阳的质量以这样的方式弯曲时空,使得在四维的时空中地球虽然沿着直线的路径运动,它却让我们看起来是沿着三维空间中的一个圆周轨道运动。时空是弯曲的事实再次意味着,光线在空间中看起来不是沿着直线旅行。这样,广义相对论预言光线必须被引力场折弯。譬如,理论预言,由于太阳的质量的缘故,太阳近处的点的光锥会向内稍微弯折。这表明,从遥远恒星发出的刚好通过太阳附近的光线会被偏折很小的角度,对于地球上的观察者而言,这恒星似乎位于不同的位置。在正常情况下,要观察到这个效应非常困难,这是由于太阳的光线使得人们不可能观看天空上出现在太阳附近的恒星。然而,在日食时就可能观察到,这时太阳的光线被月亮遮住了。</p><p>广义相对论的另一个预言是,在像地球这样的大质量的物体附近,时间显得流逝得更慢一些。这是因为光能量和它的频率(光在每秒钟里波动的次数)有一种关系:能量越大,则频率越高。当光从地球的引力场往上行进,它失去能量,因而其频率下降。考虑一对双生子。假定其中一个孩子去山顶上生活,而另一个留在海平面,第一个将比第二个老得快些。在这个例子中,年纪的差别会非常小。但是,如果有一个孩子在以近于光速运动的航天飞船中作长途旅行,这种差别就会大得多。当他回来时,他会比留在地球上另一个年轻得多。这叫做双生子佯谬。在相对论中并没有唯一的绝对时间,相反,每个人都有他自己的时间测度,这依赖于他在何处并如何运动。</p><p>爱因斯坦广义相对论意味着,宇宙必须有个开端,并且可能有个终结。</p><h3 id="膨胀的宇宙"><a href="#膨胀的宇宙" class="headerlink" title="膨胀的宇宙"></a>膨胀的宇宙</h3><p>1924 年,我们现代的宇宙图象才被奠定。那一年,美国天文学家埃德温·哈勃证明了,我们的星系不是唯一的星系。对于近处的恒星,我们可以测量其视亮度和距离,这样我们可以算出它的光度。相反,如果我们知道其他星系中恒星的光度,我们可用测量它们的视亮度来算出它们的距离。恒星离开我们是如此之遥远,使我们只能看到极小的光点,而看不到它们的大小和形状。这样怎么能区分不同的恒星种类呢?对于绝大多数的恒星而言,只有一个特征可供观测——光的颜色。牛顿发现,如果太阳光通过一个称为棱镜的三角形状的玻璃块,就会被分解成像在彩虹中一样的分颜色(它的光谱)。将一台望远镜聚焦在一个单独的恒星或星系上,人们就可类似地观察到从这恒星或星系来的光谱。</p><p>当恒星离开我们而去时,它们的光谱向红端移动(红移);而当恒星趋近我们而来时,光谱则被蓝移。这个称作多普勒效应的频率和速度的关系是我们日常熟悉的例如听一辆小汽车在路上驶过:当它趋近时,它的发动机的音调变高(对应于声波的短波长和高频率);当它经过我们身边而离开时,它的音调变低.在哈勃证明了其他星系存在之后的几年里,他花时间为它们的距离编目以及观察它们的光谱,那时候大部分人都以为,这些星系完全是随机地运动的,所以预料会发现和红移光谱一样多的蓝移光谱。因此,当他发现大部分星系是红移的:几乎所有都远离我们而去时,确实令人十分惊异!1929 年哈勃发表的结果更令人惊异:甚至星系红移的大小也不是随机的,而是和星系离开我们的距离成正比。或换句话讲,星系越远,它离开我们运动得越快!这表明宇宙不能像人们原先所想象的那样处于静态,而实际上是在膨胀;不同星系之间的距离一直在增加着。</p><p>利用多普勒效应,可由测量星系离开我们的速度来确定现在的膨胀速度。这可以非常精确地实现。然而,因为我们只能间接地测量星系的距离,所以它们的距离知道得不很清楚。我们知道的不过是,宇宙在每 10 年里膨胀 5%~ 10%。我们不能排除这样的可能性,可能还有我们尚未探测到的其他的物质形式,它们几乎均匀地分布于整个宇宙中,它仍可能使得宇宙的平均密度达到停止膨胀所必需的临界值。所以,现在的证据暗示,宇宙可能会永远地膨胀下去。但是,所有我们能真正肯定的是,既然它已经至少膨胀了 100 亿年,即便宇宙将要坍缩,至少要再过这么久才有可能。</p><p>就我们而言,大爆炸之前的事件不能有后果,所以并不构成我们宇宙的科学模型的一部分。因此,我们应将它们从模型中割除掉,并宣称时间是从大爆炸开始的。很多人不喜欢时间有个开端的观念,可能是因为它略带有神的干涉的味道。如果广义相对论是正确的,宇宙可以有过奇点,一个大爆炸。然而,它没有解决关键的问题:广义相对论是否预言我们的宇宙一定有过大爆炸或时间的开端?对于这个问题,英国数学家兼物理学家罗杰·彭罗斯在 1965 年以完全不同的手段给出了回答。利用广义相对论中光锥行为的方式以及引力总是吸引这个事实,他证明了,坍缩的恒星在自己的引力作用下陷入到一个区域之中,其表面最终缩小到零。并且由于这区域的表面缩小到零,它的体积也应如此。恒星中的所有物质将被压缩到一个零体积的区域里,所以物质的密度和时空的曲率变成无限大。换言之,人们得到了一个奇点,它被包含在一个叫做黑洞的时空区域中。如果人们将彭罗斯定理中的时间方向颠倒以使坍缩变成膨胀,假定现在宇宙在大尺度上大体类似弗里德曼模型,这定理的条件仍然成立。彭罗斯定理已经指出,任何坍缩星体必定终结于一个奇点;其时间颠倒的论证则是,任何类弗里德曼膨胀宇宙一定是从一个奇点开始。</p><p>但是一旦考虑了量子效应,奇点就会消失。</p><h3 id="不确定性原理"><a href="#不确定性原理" class="headerlink" title="不确定性原理"></a>不确定性原理</h3><p>为了预言一个粒子未来的位置和速度,人们必须能够准确地测量它现在的位置和速度。显而易见的办法是将光照到这粒子上.一部分光波被此粒子散射开来,由此指明它的位置。然而,人们不可能将粒子的位置确定到比光的两个波峰之间距离更小的程度,所以为了精确测量粒子的位置,必须用短波长的光。可是,由普朗克的量子假设,人们不能用任意小量的光;人们至少要用一个光量子。这量子会扰动这粒子,并以一种不能预见的方式改变粒子的速度。此外,位置测量得越准确,所需的波长就越短,单个量子的能量就越大,这样粒子的速度就被扰动得越厉害。换言之,你对粒子的位置测量得越准确,你对速度的测量就越不准确,反之亦然。海森伯指出,粒子位置的不确定性乘以粒子质量再乘以速度的不确定性不能小于一个确定量,该确定量称为普朗克常量。并且,这个极限既不依赖于测量粒子位置和速度的方法,也不依赖于粒子的种类。海森伯不确定性原理是世界的一个基本的不可回避的性质。</p><p>不确定性原理使拉普拉斯的科学理论,即一个完全确定性的宇宙模型的梦想寿终正寝:如果人们甚至不能准确地测量宇宙现在的状态,那么就肯定不能准确地预言将来的事件!20 世纪 20 年代,在不确定性原理的基础上,海森伯、厄文·薛定谔和保尔·狄拉克运用这种手段将力学重新表述成称为量子力学的新理论。在此理论中,粒子不再分别有很好定义的而又不能被观测的位置和速度。取而代之,粒子具有位置和速度的一个结合物,即量子态。一般而言,量子力学并不对一次观测确定地预言一个单独的结果。取而代之,它预言一组可能发生的不同结果,并告诉我们每个结果出现的概率。因而量子力学把非预见性或随机性的不可避免因素引进了科学。</p><p>在量子力学中存在着波和粒子的二重性:为了某些目的将考虑粒子成波是有用的,而为了其他目的最好将波考虑成粒子。这导致一个很重要的结果,人们可以观察到两束波或粒子之间的所谓的干涉。那也就是,一束波的波峰可以和另一束波的波谷相重合。这两束波就相互抵消,而不像人们预料的那样,叠加在一起形成更强的波。一个光干涉的熟知例子是,肥皂泡上经常能看到颜色。这是因为从形成泡的很薄的水膜的两边的光反射引起的。白光由所有不同波长或颜色的光波组成,在从水膜一边反射回来的具有一定波长的波的波峰和从另一边反射的波谷相重合时,对应于此波长的颜色就不在反射光中出现,所以反射光就显得五彩缤纷。由于量子力学引进的二重性,粒子也会产生干涉。所谓的双缝实验即是著名的例子。</p><p>爱因斯坦广义相对论制约了宇宙的大尺度结构。它没有到考虑量子力学的不确定性原理,而为了和其他理论一致这是必需的。因为我们通常检验到的引力场非常弱,所以这个理论并没导致和观测的偏离,然而,早先讨论的奇点定理指出,至少在两种情形下引力场会变得非常强:黑洞和大爆炸。在这样强的场里,量子力学效应应该是非常重要的。因此,在某种意义上,经典广义相对论由于预言无限大密度的点而预示了自身的垮台,我们还没有一个完备的协调的统一广义相对论和量子力学的理论。</p><h3 id="基本粒子和自然的力"><a href="#基本粒子和自然的力" class="headerlink" title="基本粒子和自然的力"></a>基本粒子和自然的力</h3><p>最初,人们认为原子核是由电子和不同数量的带正电的叫做质子的粒子组成。1932 年詹姆斯·查德威克发现,原子核还包含另外称为中子的粒子,中子几乎具有和质子一样大的质量但不带电荷。直到大约 30 年以前,人们还以为质子和中子是“基本”粒子。但是,质子和另外的质子或电子高速碰撞的实验表明,它们事实上是由更小的粒子构成的。加州理工学院的牟雷·盖尔曼将这些粒子命名为夸克。现在我们知道,不管是原子还是其中的质子和中子都不是不可分的。问题在于什么是真正的基本粒子——构成世界万物的最基本的构件?由于光波波长比原子的尺度大得多,我们不能期望以通常的方法去“看”一个原子的部分。我们必须用某些波长短得多的东西。正如我们在上一章所看到的,量子力学告诉我们,实际上所有粒子都是波,粒子的能量越高,则其对应的波的波长越短。</p><p>用上一章讨论的波粒二象性,包括光和引力的宇宙中的一切都能以粒子来描述。这些粒子有一种称为自旋的性质。考虑自旋的一个方法是将粒子想象成围绕着一个轴自转的小陀螺。然而,这可能会引起误会,因为量子力学告诉我们,粒子并没有任何轮廓分明的轴。粒子的自旋真正告诉我们的是,从不同的方向看粒子是什么样子的。宇宙间所有已知的粒子可以分成两组:自旋为二分之一的粒子,它们组成宇宙中的物质;自旋为 0、1 和 2 的粒子,正如我们将要看到的,它们在物质粒子之间产生力。物质粒子服从所谓的泡利不相容原理。泡利不相容原理是说,两个类似的粒子不能存在于相同的态中,也就是说,在不确定性原理给出的限制下,它们不能同时具有相同的位置和速度。不相容原理是非常关键的,因为它解释了为何物质粒子,在自旋为 0、1 和 2 的粒子产生的力的影响下,不会坍缩成密度非常高的状态的原因:如果物质粒子几乎处在相同的位置,则它们必须有不同的速度,这意味着它们不会长时间存在于相同的位置。如果世界在没有不相容原理的情形下创生,夸克将不会形成分离的轮廓分明的质子和中子,进而这些也不可能和电子形成分离的轮廓分明的原子。它们全部都会坍缩形成大致均匀的稠密的“汤”。</p><p>直到保罗·狄拉克在 1928 年提出一个理论,人们才对电子和其他自旋二分之一的粒子有了正确的理解。预言了电子必须有它的配偶——反电子或正电子。任何粒子都有会和它相湮灭的反粒子。(对于携带力的粒子,反粒子即为其自身)。在量子力学中,所有物质粒子之间的力或相互作用都认为是由自旋为整数 0、1 或 2 的粒子携带。</p><p>携带力的粒子按照其强度以及与其相互作用的粒子可以分成四个种类。大部分物理学家希望最终找到一个统一理论,该理论将四种力解释为一个单独的力的不同方面。</p><p>第一种力是引力,这种力是万有的,也就是说,每一个粒子都因它的质量或能量而感受到引力。以量子力学的方法来看待引力场,人们把两个物质粒子之间的力描述成由称作引力子的自旋为 2 的粒子携带的。它自身没有质量,所以携带的力是长程的。太阳和地球之间的引力可以归结为构成这两个物体的粒子之间的引力子交换。实引力子构成了经典物理学家称之为引力波的东西,它是如此之弱——并且要探测到它是如此之困难,以至于还从来未被观测到过。</p><p>另一种力是电磁力。它作用于带电荷的粒子(例如电子和夸克)之间,但不和不带电荷的粒子(例如引力子)相互作用。它比引力强得多然而,存在两种电荷——正电荷和负电荷。同种电荷之间的力是相互排斥的,而异种电荷之间的力则是相互吸引的。一个大的物体,譬如地球或太阳,包含了几乎等量的正电荷和负电荷。这样,由于单独粒子之间的吸引力和排斥力几乎全被抵消了,因此两个物体之间净的电磁力非常小。然而,电磁力在原子和分子的小尺度下起主要作用。</p><p>第三种力称为弱核力。它负责放射性现象,并只作用于自旋为二分之一的所有物质粒子,而对诸如光子、引力子等自旋为 0、1 或 2 的粒子不起作用。直到 1967 年伦敦帝国学院的阿伯达斯·萨拉姆和哈佛的史蒂芬·温伯格提出了弱作用和电磁作用的统一理论后,弱作用才被很好地理解。他们提出,除了光子,还存在其他 3 个自旋为 1 的被统称作重矢量玻色子的粒子,它们携带弱力.1983 年在 CERN(欧洲核子研究中心)发现了具有被正确预言的质量和其他性质的光子的 3 个粒子,也被称为人们戏称为上帝粒子.</p><p>第四种力是强核力。它将质子和中子中的夸克束缚在一起,并将原子核中的质子和中子束缚在一起。人们相信,称为胶子的另一种自旋为 1 的粒子携带强作用力,它只能与自身以及与夸克相互作用。夸克只能存在于无色的组合之中。红、绿和蓝夸克被胶子束缚形成一个“白”中子。在正常能量下,强核力确实很强,它将夸克紧紧地捆在一起。但是,大型粒子加速器的实验指出,强作用力在高能量下变得弱得多,夸克和胶子的行为就几乎像自由粒子那样。</p><p>地球上的物质主要是由质子和中子,进而由夸克构成。除了少数由物理学家在大型粒子加速器中产生的以外,不存在由反夸克构成的反质子和反中子。我们从宇宙线中得到的证据表明,我们星系中的所有物质也是这样:除了少数当粒子和反粒子对进行高能碰撞时产生的以外,没有发现反质子和反中子如果在我们星系中有很大区域的反物质,则可以预料,在正反物质的边界会观测到大量的辐射。许多粒子在那里和它们的反粒子相碰撞、相互湮灭并释放出高能辐射。</p><p>统一电磁力和弱核力的成功,使人们多次试图将这两种力和强核力合并在所谓的大统一理论(或 GUT)之中。1956 年,两位美国物理学家李政道和杨振宁提出弱作用实际上不服从 P 对称。换言之,弱力使得宇宙和宇宙的镜像以不同的方式发展。同一年,他们的一位同事吴健雄证明了他们的预言是正确的。她把放射性原子的核排列在磁场中,使它们的自旋方向一致。实验表明,在一个方向比另一方向发射出更多的电子。次年,李和杨为此获得诺贝尔奖。</p><p>早期宇宙肯定是不服从 T 对称的:随着时间前进,宇宙膨胀——如果它往后倒退,则宇宙收缩。而且,由于存在着不服从 T 对称的力,因此当宇宙膨胀时,相对于将电子变成反夸克,这些力将更多的反电子变成夸克。然后,随着宇宙膨胀并冷却下来,反夸克就和夸克湮灭,但由于已有的夸克比反夸克多,少量过剩的夸克就留了下来。正是它们构成我们今天看到的物质,由这些物质构成了我们自身。</p><p>大统一理论不包括引力。在我们处理基本粒子或原子问题时这关系不大,因为引力是如此之弱,通常可以忽略它的效应。然而,它的作用既是长程的,又总是吸引的事实,表明它的所有效应是叠加的。所以,对于足够大量的物质粒子,引力会比其他所有的力都更重要。这就是为什么正是引力决定了宇宙的演化的缘故。甚至对于恒星大小的物体,引力的吸引会超过所有其他的力,并使恒星坍缩。</p><h3 id="黑洞"><a href="#黑洞" class="headerlink" title="黑洞"></a>黑洞</h3><p>黑洞这一术语是不久以前才出现的。1969 年美国科学家约翰·惠勒,为了形象地描述至少可回溯到 200 年前的一个观念时,杜撰了这个名词。</p><p>那时候,共有两种光理论:一种是牛顿赞成的光的微粒说;另一种是光由波构成的波动说。我们现在知道,这两者在实际上都是正确的由于量子力学的波粒二象性,光既可认为是波,也可认为是粒子。在光的波动说中,不清楚光对引力如何响应。但是如果光是由粒子组成的,人们可以预料,它们正如同炮弹、火箭和行星一样受引力的影响。</p><p>1783 年,剑桥的学监约翰·米歇尔在这个假定的基础上,于《伦敦皇家学会哲学学报》上发表了一篇文章。他指出,一个质量足够大并足够致密的恒星会有如此强大的引力场,甚至连光线都不能逃逸:任何从恒星表面发出的光,在还没到达远处前就会被恒星的引力吸引回来。米歇尔暗示,可能存在大量这样的恒星,虽然由于从它们那里发出的光不会到达我们这里,我们不能看到它们;但是我们仍然可以感到它们引力的吸引。这正是我们现在称为黑洞的物体。</p><p>为了理解黑洞是如何形成的,我们首先需要理解恒星的生命周期起初,大量的气体(绝大部分为氢)受自身的引力吸引,而开始向自身坍缩而形成恒星、当它收缩时,气体原子越来越频繁地以越来越大的速度相互碰撞——气体的温度上升。最后,气体变得如此之热,以至于当氢原子碰撞时,它们不再弹开而是聚合形成氦。如同一个受控氢弹爆炸,反应中释放出来的热使得恒星发光。这附加的热又使气体的压力升高,直到它足以平衡引力的吸引,这时气体停止收缩。然而,恒星最终会耗尽它的氢和其他核燃料。貌似大谬,其实不然的是,恒星初始的燃料越多,它则被越快燃尽。这是因为恒星的质量越大,它就必须越热才足以抵抗引力。而它越热,它的燃料就被耗得越快。我们的太阳大概足够再燃烧 50 多亿年,但是质量更大的恒星可以在 1 亿年这么短的时间内耗尽其燃料,这个时间尺度比宇宙的年龄短得多了。当恒星耗尽了燃料,它开始变冷并收缩。随后发生的情况只有等到 20 世纪 20 年代末才首次被人们理解。</p><p>1928 年,一位印度研究生——萨拉玛尼安·昌德拉塞卡,算出了在耗尽所有燃料之后,多大的恒星仍然可以对抗自己的引力而维持本身。这个思想是说:当恒星变小时,物质粒子相互靠得非常近,而按照泡利不相容原理,它们必须有非常不同的速度。这使得它们相互散开并企图使恒星膨胀。昌德拉塞卡意识到,不相容原理所能提供的排斥力有一个极限。相对论把恒星中的粒子的最大速度差限制为光速。这对大质量恒星的最终归宿具有重大的意义。如果一颗恒星的质量比昌德拉塞卡极限小,它最后会停止收缩,并且变成一种可能的终态——“白矮星”。恒星还存在另一种可能的终态。其极限质量大约也为太阳质量的一倍或二倍,但是其体积甚至比白矮星还小得多。这些恒星是由中子和质子之间,而不是电子之间的不相容原理排斥力支持的,所以它们叫做中子星。另一方面,质量比昌德拉塞卡极限还大的恒星在耗尽其燃料时,会出现一个很大的问题。在某种情形下,它们会爆炸或设法抛出足够的物质,使它们的质量减小到极限之下,以避免灾难性的引力坍缩。但是很难令人相信,不管恒星有多大,这总会发生。”昌德拉塞卡指出,不相容原理不能够阻止质量大于昌德拉塞卡极限的恒星发生坍缩。但是,根据广义相对论,这样的恒星会发生什么情况呢?1939 年一位美国的年轻人罗伯特·奥本海默首次解决了这个问题。</p><p>现在,我们从奥本海默的工作中得到一幅这样的图象:恒星的引力场改变了光线在时空中的路径,使之和如果没有恒星情况下的路径不一样。光锥是表示闪光从其顶端发出后在时空中传播的路径。光锥在恒星表面附近稍微向内弯折。在日食时观察从遥远恒星发出的光线,可以看到这种偏折现象。随着恒星收缩,其表面的引力场变得更强大,而光锥向内偏折得更多。这使得光线从恒星逃逸变得。更为困难,对于远处的观察者而言,光线变得更黯淡更红。最后,当恒星收缩到某一临界半径时,表面上的引力场变得如此之强,使得光锥向内偏折得这么厉害,以至于光线再也逃逸不出去。根据相对论,没有东西能行进得比光还快。这样,如果光都逃逸不出来,其他东西更不可能;所有东西都会被引力场拉回去。这样,存在一个事件的集合或时空区域,光或任何东西都不可能从该区域逃逸而到达远处的观察者现在我们将这区域称作黑洞,将其边界称作事件视界,而它和刚好不能从黑洞逃逸的光线的那些路径相重合。</p><p>事件视界,也就是时空中不可逃逸区域的边界,其行为犹如围绕着黑洞的单向膜:物体,譬如粗心的航天员,能通过事件视界落到黑洞里去,但是没有任何东西可以通过事件视界而逃离黑洞。任何东西或任何人,一旦进入事件视界,就会很快地到达无限致密的区域和时间的终点。</p><p>在宇宙的漫长历史中,很多恒星肯定烧尽了它们的核燃料并坍缩了:黑洞的数目甚至比可见恒星的数目要大得多。仅仅在我们的星系中,大约总共有 1000 亿颗可见恒星。这样巨大数量的黑洞的额外引力就能解释为何目前我们的星系以现有的速率转动。</p><h3 id="黑洞不是这么黑的"><a href="#黑洞不是这么黑的" class="headerlink" title="黑洞不是这么黑的"></a>黑洞不是这么黑的</h3><p>如果从事件视界(亦即黑洞边界)来的光线永不相互靠近,则事件视界的面积可以保持不变或者随时间增大,但它永远不会减小——因为这意味着至少边界上的一些光线必须互相靠近。事实上,每当物质或辐射落到黑洞中去,这面积就会增大;或者如果两个黑洞碰撞并合并成一个单独的黑洞,这最后的黑洞的事件视界面积就会大于或等于原先黑洞事件视界面积的总和。事件视界面积的非减性质给黑洞的可能行为加上了重要的限制。</p><p>人们非常容易从黑洞面积的非减行为联想起被叫做熵的物理量的行为。熵是测量一个系统的无序的程度。常识告诉我们,如果不进行外部干涉,事物总是倾向于增加它的无序度。热力学第二定律是这个观念的一个准确描述。它陈述道:一个孤立系统的熵总是增加的,并且将两个系统连接在一起时,其合并系统的熵大于所有单独系统熵的总和。</p><p>如果黑洞具有某一特征,黑洞外的观察者因之可知道它的熵,并且只要携带熵的物体一落入黑洞,它就会增加,那将是很美妙的。紧接着上述的黑洞面积定理的发现,即只要物体落入黑洞,它的事件视界面积就会增加,普林斯顿大学一位名叫雅可布·柏肯斯坦的研究生提出,事件视界的面积即是黑洞熵的量度。由于携带熵的物质落到黑洞中时,它的事件视界的面积会增加,这样就使黑洞外物质的熵和事件视界面积的和永远不会降低。</p><p>黑洞辐射的思想是这种预言的第一例,它以基本的方式依赖于本世纪两个伟大理论,即广义相对论和量子力学。因为它推翻了已有的观点,所以一开始就引起了许多反对。然而,最终包括约翰·泰勒在内的大部分人都得出结论:如果我们关于广义相对论和量子力学的其他观念是正确的,那么黑洞必须像热体那样辐射。这样,即使我们还不能找到一个太初黑洞,大家相当普遍地同意,如果找到的话,它必须正在发射出大量的伽马射线和 X 射线。</p><p>黑洞辐射的存在似乎意味着,引力坍缩不像我们曾经认为的那样是最终的、不可逆转的。如果一个航天员落到黑洞中去,黑洞的质量将增加,但是最终这额外质量的等效能量将会以辐射的形式回到宇宙中去。这样,此航天员在某种意义上被“再循环”了。然而,这是一种非常可怜的不朽,因为当航天员在黑洞里被撕开时,他的任何个人的时间的概念几乎肯定都达到了终点!甚至最终从黑洞辐射出来的粒子的种类,一般来说都和构成这航天员的不同:这航天员所遗留下来的仅有特征是他的质量或能量。</p><h3 id="宇宙的起源和命运"><a href="#宇宙的起源和命运" class="headerlink" title="宇宙的起源和命运"></a>宇宙的起源和命运</h3><p>从爱因斯坦广义相对论本身就能预言:时空在大爆炸奇点处开始,并会在大挤压奇点处(如果整个宇宙坍缩的话)或在黑洞中的一个奇点处(如果一个局部区域,譬如恒星坍缩的话)结束。任何落进黑洞的东西都会在奇点处毁灭,在外面只能继续感觉到它的质量的引力效应。另一方面,当考虑量子效应时,物体的质量和能量似乎会最终回到宇宙的其余部分,黑洞和在它当中的任何奇点会一道蒸发掉并最终消失。</p><p>就在大爆炸时,宇宙体积被认为是零,所以是无限热,但是,辐射的温度随着宇宙的膨胀而降低。大爆炸后的 1 秒钟,温度降低到约为 100 亿度,这大约是太阳中心温度的 1000 倍,亦即氢弹爆炸达到的温度。在大爆炸后的大约 100 秒,温度降到了 10 亿度,也即最热的恒星内部的温度。在此温度下,质子和中子不再有足够的能量逃脱强核力的吸引,所以开始结合产生氘(重氢)的原子核。氘核包含一个质子和一个中子。然后,氘核和更多的质子、中子相结合形成氦核,它包含两个质子和两个中子,还产生了少量的两种更重的元素锂和铍。大爆炸后的几个钟头之内,氦和其他元素的产生就停止了。之后的 100 万年左右,宇宙仅仅是继续膨胀,没有发生什么事。最后,一旦温度降低到几千度,电子和核子不再有足够能量去战胜它们之间的电磁吸引力,就开始结合形成原子。宇宙作为整体,继续膨胀变冷,但在一个比平均稍微密集些的区域,膨胀就会由于额外的引力吸引而缓慢下来。在一些区域膨胀最终会停止并开始坍缩。最终,当区域变得足够小,它自转得快到足以平衡引力的吸引,碟状的旋转星系就以这种方式诞生了。另外一些区域刚好没有得到旋转,就形成了叫做椭圆星系的椭球状物体。这些区域之所以停止坍缩,是因为星系的个别部分稳定地围绕着它的中心公转,但星系整体并没有旋转。恒星的外部区域有时会在称为超新星的巨大爆发中吹出来,这种爆发使星系中的所有恒星在相形之下显得黯淡无光。恒星接近生命终点时产生的一些重元素就被抛回到星系里的气体中去,为下一代恒星提供一些原料。因为我们的太阳是第二代或第三代恒星,是大约 50 亿年前由包含有更早超新星碎片的旋转气体云形成的,所以大约包含 2%这样的重元素。云里的大部分气体形成了太阳或者喷到外面去,但是少量的重元素集聚在一起,形成了像地球这样的,现在作为行星围绕太阳公转的物体。</p><p>地球原先是非常热的,并且没有大气。在时间的长河中它冷却下来,并从岩石中散发气体得到了大气。我们无法在这早先的大气中存活。因为它不包含氧气,反而包含很多对我们有毒的气体,如硫化氢。然而,存在其他能在这种条件下繁衍的原始的生命形式。人们认为,它们可能是作为原子的偶然结合,形成叫做高分子的大结构的结果,而在海洋中发展,这种结构能够将海洋中的其他原子聚集成类似的结构。第一种原始的生命形式消化了包括硫化氢在内的不同物质,而释放出氧气。这就逐渐地将大气改变成今天这样的成分,并且允许诸如鱼、爬行动物、哺乳动物以及最后人类等生命的更高形式的发展。</p><h3 id="时间箭头"><a href="#时间箭头" class="headerlink" title="时间箭头"></a>时间箭头</h3><p>当人们试图统一引力和量子力学时,必须引入“虚”时间的概念。虚时间是不能和空间方向区分的。如果一个人能往北走,他就能转过头并朝南走;同样的,如果一个人能在虚时间里向前走,他应该能够转过来并往后走。这表明在虚时间里,往前和往后之间不可能有重要的差别。过去和将来之间的这种差别从何而来?为何我们记住过去而不是将来?科学定律并不区别过去和将来。更精确地讲,正如前面解释的,科学定律在称作 C、P 和 T 的联合作用(或对称)下不变。(C 是指用反粒子替代粒子。P 的意思是取镜像,这样左和右就相互交换了。而 T 是指颠倒所有粒子的运动方向:事实上,是使运动倒退回去。)</p><p>想象一杯水从桌子上滑落下,在地板上被打碎。如果你将其录像,你可以容易地辨别出它是向前进还是向后退。如果将其倒放回来,你会看到碎片忽然集中到一起离开地板,并跳回到桌子上形成一个完整的杯子。你可断定录像是在倒放,因为在日常生活中从未见过这种行为。</p><p>为何我们从未看到破碎的杯子集合起来,离开地面并跳回到桌子上,通常的解释是这违背了热力学第二定律。它可表述为,在任何闭合系统中无序度或熵总是随时间而增加。地板上破碎的杯子是一个无序的状态。人们很容易从早先桌子上的杯子变成后来地面上的碎杯子,而不是相反。无序度或熵随着时间增加是所谓的时间箭头的一个例子:时间箭头将过去和将来区别开来,使时间有了方向。至少有三种不同的时间箭头:首先是热力学时间箭头,即是在这个时间方向上无序度或熵增加;然后是心理学时间箭头,这就是我们感觉时间流逝的方向,在这个方向上我们可以记忆过去而不是未来;最后,是宇宙学时间箭头,宇宙在这个方向上膨胀,而不是收缩。</p><p>假定上帝决定不管宇宙从何状态开始,它都必须结束于一个高度有序的状态,则在早期这宇宙很可能处于无序的状态。这意味着无序度将随时间而减小。你将会看到破碎的杯子集合起来并跳回到桌子上。然而,任何观察杯子的人都生活在无序度随时间减小的宇宙中,我将论断这样的人会有一个倒溯的心理学时间箭头。这就是说,他们会记住将来的事件,而不是过去的事件。当杯子被打碎时,他们会记住它在桌子上的情形;但是当它在桌子上时,他们不会记住它在地面上的情景。</p><p>记忆器从无序态转变成有序态。然而,为了保证记忆器处于正确的状态,需要使用一定的能量(例如,移动算盘珠或给计算机接通电源)。这能量以热的形式耗散了,从而增加了宇宙的无序度的量。人们可以证明,这个无序度增量总比记忆器本身有序度的增量大。这样,由计算机冷却风扇排出的热量表明计算机将一个项目记录在它的记忆器中时,宇宙的无序度的总量仍然增加。计算机记忆过去的时间方向和无序度增加的方向是一致的。因此我们对时间方向的主观感觉或心理学时间箭头,是在我们头脑中由热力学时间箭头决定的。</p><h3 id="虫洞和时间旅行"><a href="#虫洞和时间旅行" class="headerlink" title="虫洞和时间旅行"></a>虫洞和时间旅行</h3><p>1949 年库尔特·哥德尔发现了广义相对论允许的新的时空。这首次表明物理学定律的确允许人们在时间里旅行。</p><p>因为时间不存在唯一的标准,而每一位观察者都拥有他自己的时间。这种时间是用他携带的时钟来测量的,这样航程对于空间旅行者比对于留在地球上的人显得更短暂是可能的。但是,这对于那些只老了几岁的返回的空间旅行者,并没有什么值得高兴的,因为他发现留在地球上的亲友们已经死去几千年了。</p><p>要打破光速壁垒存在一些问题。相对论告诉我们,飞船的速度越接近光速,用以对它加速的火箭功率就必须越来越大。对此我们已有实验的证据,但不是航天飞船的经验,而是在诸如费米实验室或者欧洲核子研究中心的粒子加速器中的基本粒子的经验。我们可以把粒子加速到光速的 99.99%,但是不管我们注入多少功率,也不可能把它们加速到超过光速壁垒。航天飞船的情形也是类似的:不管火箭有多大功率,也不可能加速到光速以上。这样看来,快速空间旅行和逆时旅行似乎都不可行了。然而,还可能有办法。人们也许可以把时空卷曲起来,使得 A 和 B 之间有一近路。在 A 和 B 之间创生一个虫洞就是一个法子。顾名思义,虫洞就是一个时空细管,它能把两个相隔遥远的几乎平坦的区域连接起来。</p><p>时空不同区域之间的虫洞的思想并非科学幻想作家的发明,它的起源是非常令人尊敬的。<br>1935 年爱因斯坦和纳珍·罗森写了一篇论文。在该论文中他们指出广义相对论允许他们称为“桥”,而现在称为虫洞的东西。但它们不能保持畅通足够久,以使任何东西通过。</p><p>假定你回到过去并且将你的曾曾祖父在他仍为孩童时杀死。这类佯谬有许多版本,但是它们根本上是等效的:如果一个人可以自由地改变过去,则他就会遇到矛盾。看来有两种方法解决由时间旅行导致的佯谬。一种称为协调历史方法。它是讲,甚至当时空被卷曲得可能旅行到过去时,在时空中发生的必须是物理定律的协调的解。根据这个观点,除非历史表明,你曾经到达过去,并且当时并没有杀死你的曾曾祖父或者没有干过任何事和你的现状相冲突,你才能在时间中回到过去。此外,当你回到过去,你不能改变记载的历史。那表明你并没有自由意志为所欲为。解决时间旅行的其他可能的方法可称为选择历史假说。其思想是,当时间旅行者回到过去,他就进入和记载的历史不同的另外历史中去。这样,他们可以自由地行动,不受和原先的历史相一致的约束。</p>]]></content>
<summary type="html">
<h1 id="时间简史"><a href="#时间简史" class="headerlink" title="时间简史"></a>时间简史</h1><blockquote>
<p>摘录自 史蒂芬霍金 &lt;&lt;时间简史&gt;&gt;</p>
</blockquote>
</summary>
<category term="读书 笔记" scheme="http://yoursite.com/tags/%E8%AF%BB%E4%B9%A6-%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>西方哲学史</title>
<link href="http://yoursite.com/2021/12/22/%E8%A5%BF%E6%96%B9%E5%93%B2%E5%AD%A6%E5%8F%B2/"/>
<id>http://yoursite.com/2021/12/22/西方哲学史/</id>
<published>2021-12-22T09:22:52.000Z</published>
<updated>2022-03-23T09:49:11.569Z</updated>
<content type="html"><![CDATA[<h1 id="西方哲学史"><a href="#西方哲学史" class="headerlink" title="西方哲学史"></a>西方哲学史</h1><blockquote><p>摘录自 林欣浩 <<哲学家们都干了些什么?>></p></blockquote><h3 id="苏格拉底与柏拉图"><a href="#苏格拉底与柏拉图" class="headerlink" title="苏格拉底与柏拉图"></a>苏格拉底与柏拉图</h3><p>苏格拉底在临终前说,未经审视的人生是不值得过的。不过,如果交谈的目的只在于获取定义,那么就算能反思人生,也算是失败。得到确切的定义很可能并非交谈的重点或目的,或许共同探讨本身才是目的,审视问题才是价值所在。</p><p>苏格拉底没有写过哲学方面的著作。不过,我们可以参考柏拉图的早期对话录——其中一些片段表达了苏格拉底的观点。对话录通常分为至少三个阶段,但是究竟按什么顺序排列仍然存在争议。苏格拉底在大部分对话中都是主人公。据说,所谓的“早期对话录”主要反映了苏格拉底的思想,不过到了中后期,苏格拉底其实成了柏拉图的代言人。</p><p>苏格拉底被希腊公民审判死刑, 为什么苏格拉底宁愿死,也要怀疑?因为人和动物的区别在于人要思考。而怀疑是思考的起点,也是思考成果的检验者。怀疑的最大作用在于能避免独断论,这样才能引导我们寻找正确的答案,免得我们轻信一切未经证实的结论。苏格拉底的怀疑是理性文明的开端和标尺。所有的思想都要因他的怀疑而诞生,最后还要能经得住他的怀疑才算合格。正是照着这个标准思考,西方人才有了哲学,才有了科学,才创造了现代文明。</p><h3 id="亚里士多德的演绎推理"><a href="#亚里士多德的演绎推理" class="headerlink" title="亚里士多德的演绎推理"></a>亚里士多德的演绎推理</h3><p>亚里士多德是亚历山大大帝的老师,和他一样都是马其顿人.亚历山大帝国的统一使希腊文明得以传播到东欧,北非和中亚.</p><p>亚里士多德在某种程度上发明并制定了正确的推理规则,这也是哲学逻辑的开端。他是第一个研究演绎性质的人。演绎指的是某个命题遵循必要的前提,辨别各种可能的推论并将其形式化。掌握证据的一般概念后,他界定了研究万物所得到的证据与有关这些事物的真实结论之间的关系。这样一来,他就给世人指明了科学的方向.三段论是演绎推理的一般模式,包含三个部分:大前提——已知的一般原理,小前提——所研究的特殊情况,结论——根据一般原理,对特殊情况作出判断。例如:知识分子都是应该受到尊重的,人民教师都是知识分子,所以,人民教师都是应该受到尊重的。</p><p>柏拉图认为,人们不必依靠永恒不变的形式就可以认识世界。相反,亚里士多德认为,形式和共性只能蕴藏于世界万物之内,而了解事物不仅要观察形式,而且需要亲自去检查、剖析不断变化的世界上的万事万物,从而逐渐了解其成因。</p><h3 id="保罗与基督教"><a href="#保罗与基督教" class="headerlink" title="保罗与基督教"></a>保罗与基督教</h3><p>保罗与耶稣是同一时期的,他是犹太人,信奉犹太教,他听从教长的指示,积极迫害基督徒。据《使徒行传》的记载,有一天,保罗在追捕耶稣门徒的路上突然见到天上发光,听到耶稣对他说:“为什么要逼迫我?”保罗大惊失色,眼睛失明,直到三天后才恢复视力。之后他皈依了基督教.</p><p>保罗有深厚的哲学功底,他将哲学的思维方式应用到传教中,撰写了大量的神学文章。这些文字后来被称作《保罗书信》,成为《新约》的重要组成部分。</p><p>公元 64 年 7 月 17 日夜里发生了一件大事。西方世界的中心,全欧洲最富饶、最美丽的城市罗马突然烧起了大火。这火太大了,持续烧了六天七夜,整个罗马城的三分之二都被烧为灰烬。在那个年代,基督徒们相信世界末日就快到来,有些人到处宣传“上天将会降下巨大的火球烧毁一切”。因此当时还有些人认为,罗马的大火就是基督徒放的。或许是为了洗脱自己的嫌疑,不久以后,罗马皇帝尼禄正式宣布这场大火是基督徒所放,同时展开了对基督徒大规模的逮捕和残杀。一般认为,保罗就死于这场大火之后的审判中。基督徒们从此受到了极为残酷的迫害。</p><h3 id="奥勒留与斯多亚学派"><a href="#奥勒留与斯多亚学派" class="headerlink" title="奥勒留与斯多亚学派"></a>奥勒留与斯多亚学派</h3><p>马可·奥勒留(121—180)是一位带有哲学倾向的罗马皇帝,他的作品反映了斯多亚学派哲学的精华内容,而这些内容被他的强权实践所验证。大概在他的晚年时期,尽管罗马帝国身处混乱状况,奥勒留还是完成了《沉思录》,这本书奇迹般地留存到了现在。《沉思录》显然算不上标准的哲学著作,更像是他的私人日记。书中没有针对清晰表述的观点进行持续论证,而是有很多不连贯的感言、格言以及个人忠告。这本书创作的浪漫之处在于奥勒留结束白天的战争后,每个孤独的不眠夜都会在月光下的多瑙河边写上几行字。这很可能是事实。</p><p>斯多亚学派哲学的观点,即与自然保持和谐的人生是最好的、最道德的。有一则比喻可以很好地证明上述观点,那就是拴在马车背后的一条狗。马车前进时,狗有两种选择,既可以一边大声叫,一边朝着相反方向用力,导致自己受勒,又可以选择平心静气地随马车一起前进。不管它做出哪种选择,都会朝着马车前进的方向前进,它唯一能做的就是去应对业已注定的命运。奥勒留总是在提醒自己,斯多亚学派学者必须搞清楚哪些事物受控于自身,哪些事物不受控制。奥勒留曾引用了爱比克泰德的话:“我们可以控制的方面包括观点、冲动、欲望和厌恶情绪.不能控制的包括身体、财产、名誉和官职。</p><h3 id="罗马的分裂和十字军东征"><a href="#罗马的分裂和十字军东征" class="headerlink" title="罗马的分裂和十字军东征"></a>罗马的分裂和十字军东征</h3><p>公元 284 年戴克里先被推举为罗马第一执政官,罗马皇帝.戴克里先一直在琢磨帝国千年基业的问题。他总结了之前罗马历史上的得失,认为帝国的最大弱点在于国家面积太大了,一个皇帝管不过来。他大手一挥,把罗马分成了东西两个部分。自己不当整个罗马的皇帝,只当东罗马帝国的皇帝,给对面的西罗马帝国又找了一个新皇帝。而且他觉得光这么分还不够,还是守不过来。他又找来了两个副手皇帝,自己和西罗马皇帝一人一个,把帝国进一步分成了四份。但是戴克里先退位之后,其他的皇帝发生了斗争.最够君士坦丁成为西罗马帝国皇帝,并接纳了基督教.最后君士坦丁又击败了东罗马帝国皇帝.</p><p>基督教称为罗马帝国国教之后,开始了清除希腊哲学的行动,他们烧毁了亚历山大图书馆,关闭了柏拉图学院.在基督徒烧毁神庙的时候,一些哲学家惊慌失措地向东逃跑,此时波斯帝国覆灭,他们就到了阿拉伯帝国.虽然伊斯兰教也是一神教,但是他们展示了对异教的宽容,接纳了他们,从此希腊文化在阿拉伯帝国存在了下来.并在阿拉伯占领北非的时候,一些希腊文化的学者借道北非到了西班牙.而基督徒在数次十字军大败后,开始了着手翻译各种阿拉伯文的典籍。其中也免不了包括一些希腊哲学著作。</p><h3 id="阿奎那与天主教哲学"><a href="#阿奎那与天主教哲学" class="headerlink" title="阿奎那与天主教哲学"></a>阿奎那与天主教哲学</h3><p>公元 476 年,中国南北朝的时候,西罗马帝国的最后一任皇帝被赶下台。这件事标志着西罗马帝国的灭亡,从此以后,欧洲分为多个国家,再也没有统一过.西罗马灭亡了,但基督教保留了下来,且势力越来越大.基督教神学家们为了维护自己的观点,斥责对方是异端,展开了激烈的学术辩论。这时基督教的神学家们开始用各种办法从东方获取希腊文献来寻找学术辩论的理论依据。这在欧洲又掀起了一段研究哲学的高潮。此时的哲学被称作“经院哲学”。”其中的集大成者就是阿奎那.</p><p>圣托马斯·阿奎那(约 1225—1274)是意大利天主教哲学家,也是学术传统的重要人物之一.如果不算唯一一位重要人物的话。是他创立了托马斯哲学学派,该学派在相当长的一段时期内是罗马天主教会的主要哲学支撑。直到今天,新托马斯主义还是天主教的重要思想。</p><p>阿奎那采用的亚里士多德注释方法使得他的论证非常著名。每种方法都以公认的经验事实开头,并证明上帝存在。世上万事万物都要有另一个事物作为它的原因。那么必然存在一个最初的原因,这个原因就是上帝。经院哲学家想得挺好,他们用哲学去证明宗教,为的是让宗教也能符合理性的考验。但是别忘了,怀疑是哲学的核心精神。就在这么无聊的经院哲学时期,却出现了一个对后来的科学发展极为重要的理论。它是一个教士在研究神学的时候提出来的。这个教士因为出生于一个叫作奥卡姆的地方,因此被人称为“奥卡姆的威廉”,这个理论就被后人称为“奥卡姆剃刀”,内容就是”如无必要,勿增实体”.</p><h3 id="君主论与马基雅维利主义"><a href="#君主论与马基雅维利主义" class="headerlink" title="君主论与马基雅维利主义"></a>君主论与马基雅维利主义</h3><p>马基雅弗利的外交官生涯为他提供了创作《君主论》的必要经历,但是在 1512 年不幸终止。那年,美第奇家族在佛罗伦萨掌权后,解散了共和国,之后马基雅弗利也被撤职。更糟糕的是,几个月后,马基雅弗利还遭到冤枉,被指控暗算新成立的美第奇政府,并遭到严刑拷打与监禁。他正是基于这样的形势变化创作了《君主论》。马基雅弗利之所以认为君主要想高效地统治国家就必须学会怎样作恶,其根源在于他对人性的偏见。他认为,人都是“忘恩负义、心怀二志、弄虚作假、伪装好人、见死不救和利欲熏心的.</p><h3 id="马丁路德-和-加尔文"><a href="#马丁路德-和-加尔文" class="headerlink" title="马丁路德 和 加尔文"></a>马丁路德 和 加尔文</h3><p>马丁路德(1483 年 11 月 10 日-1546 年 2 月 18 日).</p><p>罗马教会比较看重信徒是否遵守律法、纳税、履行仪式这些外在的行为,认为这些外在行为是信徒成为“义人”的关键,这种观点在神学上被称为“因行称义”。但是马丁・路德在阅读《圣经》中的保罗书信时,发现保罗所持的是“因信称义”的观点。“因信称义”的意思就是说,真心相信上帝,就可以成为“义人”。”</p><p>禁止普通百姓接触《圣经》的命令,是在 13 世纪开始颁布的。因为垄断《圣经》的好处太明显了。既然教会的全部权威都来自于这本书,那么把这本书束之高阁,也就没有人可以怀疑教会了,一切都必须以教会的说法为准。换句话说,垄断了对权威的解释,就等于垄断了一切。但是很快,中国的活字印刷术就传到了欧洲.因为有了印刷术,还因为有城市居民和手工业者等群众基础,所以马丁・路德的那些宗教檄文一经写完就在欧洲迅速传开,宗教革命终于遍及整个欧洲,千百万神父和知识分子卷入其中。几十年后,欧洲支持路德和罗马的两派贵族还打了一场惨烈的宗教战争。双方打了个势均力敌。从此,欧洲基督教分成了两大派:罗马一方被称为天主教;路德一方被称为新教。另外,东边的罗马帝国在此之前还搞了一个东正教。</p><p>加尔文比路德小十二岁。他和路德同样是先学法律,中途改为研究神学。加尔文认同路德的观点,因此受到了天主教的迫害,一路流浪,来到了瑞士的日内瓦。最终,加尔文在日内瓦确立了他在新教中的地位。加尔文将新教的影响扩大到了整个欧洲,并且用庞大、严格的教会系统维持他的统治。很快,加尔文像他的敌人——罗马的天主教皇那样——当上了新教的教皇,日内瓦成了新教的罗马。新教是靠着路德一篇篇雄辩的文字,从天主教的火刑架下顽强成长起来的。但加尔文和他的继承者们却在日内瓦竖起了更多的火刑架。他们烧天主教徒,烧异端分子,烧跟他神学观点不一致的人,这再次证明了,哲学和宗教的联合是行不通的。宗教只会把哲学当作获权的工具,一旦取得胜利,就会毫不犹豫地把哲学扔到一边。</p><h3 id="荷兰共和国和笛卡尔"><a href="#荷兰共和国和笛卡尔" class="headerlink" title="荷兰共和国和笛卡尔"></a>荷兰共和国和笛卡尔</h3><p>教会最可怕的武器是宗教裁判所。在各个国家中,最严酷、最血腥的宗教裁判所在西班牙。而西班牙的历代君王中,最不可一世的是腓力二世(也被翻译为“菲利普二世”)。他的老婆是著名的“血腥玛丽”,以屠杀异教徒而闻名。腓力二世则打赢了欧洲有史以来最大规模的海战,阻止了奥斯曼帝国进攻欧洲的企图。他还拥有欧洲最强大的“无敌舰队”,把美洲变成了西班牙的后花园。腓力二世的志向如此之大,或许觉得在小小荷兰发生的一场叛乱很容易就能搞定。但结果出乎了他的意料.</p><p>荷兰起义军的首领威廉是一个令人敬佩的贵族。一是为了保护自己的财产,二是因为他与荷兰有深厚的感情,再加上他也同情被西班牙迫害的新教徒,结果威廉毅然放弃了舒适的生活,花了大笔财产组织起义军。腓力二世不愿意小小的荷兰牵扯他太多的精力和金钱,于是他换了一种镇压方式:悬赏威廉的性命。奖赏金是赦免过去一切罪行,封为贵族,还有 25000 枚金币的巨款作为赏金。威廉被刺杀的那天,距离他登基成为荷兰国王只差两天。威廉被杀后四年,具有绝对优势的西班牙“无敌舰队”被用海盗船、商船拼凑起来的英国舰队悉数歼灭。又过了八年,腓力二世被法国击败。一连串军事和政策上的失败使得西班牙实力大减。这成就了坚持不懈顽强起义的荷兰人。威廉去世二十多年后,荷兰终于赢得了独立。</p><p>因为荷兰商业发达(雅典也是商业发达),在天主教和新教比赛般绞杀异端的世界里,荷兰拥有全欧洲最开明的言论政策。不久以后,荷兰成为各类异端分子、科学家、哲学家的避难地。它成为雅典之后的哲学的第二个故乡。荷兰在近代欧洲第一个取消了独裁者,完全采用议会投票的方式处理政务。这种政体从罗马屋大维结束民主制度以来,已经很多年没有了。此时的荷兰也不能叫作“荷兰王国”了,而改叫“荷兰共和国”。荷兰虽然商业发达,但是军事不行,于是就大量雇佣雇佣兵,其中有一个人,叫笛卡尔.</p><p>勒内·笛卡儿(1596—1650)或许是被引用最多的哲学家了,最常被引用的是那句:我思,故我在。</p><p>笛卡尔比怀疑先哲著作还要更彻底,他要彻底怀疑整个世界:我眼前的这个世界是不是都是假的?我见到的一切会不会都是幻觉、都是梦境?他想,不管我再怎么怀疑,“我怀疑”这件事是确定的,它肯定存在吧?那么,只要有了怀疑的念头,就说明“我”肯定是存在的——“我”要是不存在就不会有这些念头了。“我思”和“我在”不是因果关系,而是推理演绎的关系。即:从前者为真可以推导出后者为真。也就是从“我思”为真,可以推导出“我在”为真。而不是说“我不思”的时候就“我不在”了,在不在我们不知道。</p><h3 id="形而上学"><a href="#形而上学" class="headerlink" title="形而上学"></a>形而上学</h3><p>此时的哲学已经有了公认原则。即:我们的结论必须能经得起各种怀疑,这样才能保证它真实可信。这也是科学研究的原则。但是还有一个大问题。我们该用什么方法才能得出可靠的、经得住怀疑的结论呢?</p><p>笛卡尔从欧几里得的几何原本中得要灵感,几何原本由几条简单的公理和公设推导出厚厚的六卷.欧氏几何启发了笛卡尔时代的哲学家.为什么不像欧氏几何那样,建立一套严密、规整,又高于世间万物的理论体系.</p><p>笛卡尔的疑问关系到了哲学上的一个重要概念,叫作“形而上学”。我们在学校里学习马哲的时候,课本给我们的解释是:形而上学就是用孤立、静止、片面的方式看待问题。在课本上,“形而上学”被当成一个贬义词.它的来历是什么呢?众所周知,亚里士多德写了很多著作,一个叫安德罗尼柯的人用“研究有形体的事物”和“研究没有形体的事物”,把亚里士多德的著作分成了两大类。一个是物理学,一个是形而上学,意思是“那些高于物理学的、看不见、摸不着的学问。《易经》有一句话:“形而上者谓之道,形而下者谓之器。日本哲学家井上哲次郎先生在看到 metaphysics 这个词后,联想到《易经》,把 metaphysics 翻译成了“形而上学”。</p><p>我们现在所说的“形而上学”,可以简单地理解成是用理性思维去研究那些能统一世间一切问题的“大道理”。比如世界的本质是什么样子、人生的意义是什么之类的问题.</p><h3 id="二元论-与-唯我论"><a href="#二元论-与-唯我论" class="headerlink" title="二元论 与 唯我论"></a>二元论 与 唯我论</h3><p>笛卡尔只知道自己的意识存在,不知道外面的世界存在不存在。这个结论暗含了一个前提,那就是:他把我们讨论的世界分成两个部分,一个是我们自己的心灵,一个是心灵之外的部分。这种观点就叫作“二元论”。心灵一个元,外界一个元,一共二元。这两个元是相互独立的、平等的,虽然可以互相影响,但谁也不能完全决定另一个。</p><p>唯物主义,说世界的本质是物质的,我们的精神世界不过是大脑生理活动的结果。换句话说,精神是从物质中产生的。这种观点就叫作物质一元论。当然,相应的也有唯心主义的一元论,认为世界的本质是精神的,外面的世界不过是我自己心灵的产物罢了。</p><p>在二元论的观念下,世界被一分为二:外界和内心。痛苦虽然来自于外界,但真正承受痛苦的是我的内心。因此我们虽然仍旧需要尽力去改变外物,但在客观世界这一元里的得失其实不重要,关键是固守自己的内心这一元,固守住我们获得体验的最后一关。而在内心世界里,我们自己能完全做主,这就让人产生了很大的安全感。我们想,对人伤害最大的其实不是一时的痛苦,而是对未来痛苦的恐惧。所以,在面对痛苦的时候,我们应该把自己的感受局限在此时一瞬,而不要顾及那些未到的痛苦。我们自己其实是由无数个时间瞬间组成的。我们的感受只是此一瞬的。而这一瞬的痛苦,前面从二元论的角度讨论过了,并不难忍受。至于未来尚未到来的痛苦,此时并未加诸我身,对我也就没有伤害。</p><p>从二元论进一步,还可以得到唯我论。假设我们只停留在“我在”的阶段,我们只能确认我自己存在,外界的一切存在不存在我不知道,这就叫“唯我论”。首先,和二元论一样,唯我论很难被彻底反驳掉。我们永远都可以质疑自己生活的世界是一片幻觉,或者只是一个梦。当你思考“世界的本质是什么”的时候,唯我论永远立在一旁幽幽地望着你。挥之不去。其次,唯我论对我们的普通生活也有很大的影响。它可以让我们变得更坚强。在采用唯我论的时候,我们会感到天上地下唯我独大,我们不用害怕任何事物,只要面对自己的内心就可以了。</p><h3 id="斯宾诺莎与伦理学"><a href="#斯宾诺莎与伦理学" class="headerlink" title="斯宾诺莎与伦理学"></a>斯宾诺莎与伦理学</h3><p>笛卡尔有一个很棒的想法,就是按照欧式几何学的模式来建立哲学体系。具体来说,就是先找出一些不言自明的公设,再以这些公设为基础,按照演绎推理的方法建立整个哲学体系。笛卡尔的想法不错,具体工作却做得不太好。斯宾诺莎则完美实现了这个想法。</p><p>斯宾诺莎于 1632 年 11 月 24 日出生在阿姆斯特丹,父母都是秘密的犹太人。也就是说,他们曾被迫信仰基督教,却秘密信奉着犹太教。1670 年,《神学政治论》匿名出版了,该书认为《圣经》应该是一部基于史实、文化底蕴深厚的文献,这无疑指出了《圣经》作者的狭隘认识。举个例子,斯宾诺莎否认《圣经》中的神迹属于超自然现象,声称它们只不过是自然现象,却因为《圣经》作者的知识匮乏而产生了误解。斯宾诺莎最有影响的著作叫《伦理学》,在他去世后才发表。这本书的全称是《按几何顺序证明的伦理学》。</p><p>如何证明呢? 第一步找到公设,我们就必须找到一个绝对存在的、不可能被怀疑的东西作为公设。既然这个东西绝对存在,那么它肯定不能依赖别的物体存在。斯宾诺莎把这种东西称作“实体”。这么一个永恒的、无限的、唯一的、不可分的东西,就是上帝。斯宾诺莎就是这么想的。所以,斯宾诺莎承认上帝,但他心目中的上帝不是基督教或者犹太教中人格化的上帝,而是无所不在的实体。类似于中国哲学里很多学派都追求的“天人合一”.</p><h3 id="培根与归纳法"><a href="#培根与归纳法" class="headerlink" title="培根与归纳法"></a>培根与归纳法</h3><p>科学家们是靠什么搞研究的呢?靠归纳法。<br>这应该归功于培根,说过“知识就是力量”的培根,一位和笛卡尔同时代的知识分子。归纳法的意思是,人们通过观察多个个别的现象,总结出普遍的规律。比如人观察到,每一次把石头扔出去,最后石头总要落地。那么他就能总结出“空中的石头总会落地”这么条规律来。事实上,我们今天取得的所有科学成就,都是综合使用归纳法和演绎推理的结果。举例,科学家先观察到某些现象(比如木头用火一点就燃烧),用归纳法假设出一条科学规律来(是高温引起木头燃烧吗?),然后用演绎推理从这个假设中得到一些推论(那么用烧红的烙铁虽然没有火苗,也应该能点燃木头),再根据这些推论去做实验,看实验结果是不是符合假设的理论(哇,果然点燃了)。然后科学家就可以写篇《论木头燃烧的原因》发表了。这套科学方法里既有归纳法,也有演绎推理,但其基础、起关键作用的,是归纳法。科学家们“轻视”演绎推理,关键在于他们发现演绎推理有一个巨大的缺陷。这个缺陷就是,演绎推理不能给我们带来任何新知识。一本《几何原本》的全部知识其实就是开头的那几条公设和公理,后面厚厚的十三卷内容不过是在不断用其他的形式去重复那些公设和公理罢了。而科学的任务是探索自然界,获取新的知识。毫无疑问,数学是不可能完成这个任务的。归纳法是科学家们的唯一选择。</p><p>笛卡尔他们研究哲学,不都先要公设吗?问题是,这公设它有什么根据吗?斯宾诺莎说世上存在实体,你能做一实验给我证明吗?说白了,笛卡尔和斯宾诺莎构建的哲学世界,整个学说不过只是几句公设,而这几句公设还没什么根据!</p><h3 id="洛克-现代经验主义之父"><a href="#洛克-现代经验主义之父" class="headerlink" title="洛克:现代经验主义之父"></a>洛克:现代经验主义之父</h3><p>第一个向数学派哲学家发起挑战的科学派哲学家叫洛克。约翰·洛克(1632—1704 英国)主要关注社会问题和认识论。他的理论激励了美国革命和法国大革命.他认为,知识主要来自经验,而非天赋观念。</p><p>洛克说,刚出生的婴儿,内心就像是一张白纸或者一块白板,什么都没有,人的思想都是靠后天学习得来的。没有什么知识是人不用学习,先天就能领悟的。在洛克看来,笛卡尔、斯宾诺莎等人号称的那些公设,全都是无根之木。洛克也承认人的本能是天生的,比如直觉之类。但洛克认为,这些本能就和动物捕食、生存的本能一样,是一种生理、心理上的习惯而已,并不是什么比客观世界高一等的理性,更不可能由此建立起一个哲学世界来。洛克在《人类理智论》在中提出了物体第一性质与第二性质的区别。第一性质与物体“密不可分”,无论物体经历任何变化,第一性质都会始终不变,它也是“物体微粒”的一部分。洛克指出,固体性、延展性、形状、体积和运动属于物体的第一性质,而第二性质则“不存在于物体内部,而是通过物体第一性质使人产生各种感觉的力量”。洛克将颜色、味道、触感、气味和声音归为了第二性质。举个例子,人们对柠檬形状的认识反映了外界世界中柠檬的本来面目,但是柠檬的味道却不一定具有同等作用。”</p><h3 id="理性主义与经验主义"><a href="#理性主义与经验主义" class="headerlink" title="理性主义与经验主义"></a>理性主义与经验主义</h3><p>洛克的学说给当时的哲学界带来了很大的影响。原本数学家一枝独秀的哲学界,出现了数学派与科学派双雄争霸的场面。由于这场争论是哲学界的一件大事,所以哲学家们给这两派学说分别起了名字。笛卡尔、斯宾诺莎代表的数学家派,被称为“理性主义”。在归纳法里,最重要的是实验数据,是观测结果,它们是科学理论的基础和证据。这些东西可以用一个词来统称:经验。所以洛克代表的科学家派被称为“经验主义”。</p><p>理性主义和经验主义之间的分歧,其实可以上溯到柏拉图和亚里士多德的分歧。他们俩对世界的看法就不一样。一个重视心灵理性,一个重视现实经验。以“人”这个概念为例。柏拉图说,“人”这个概念比“张三李四”这些具体的人更真实。“张三李四”生了又死,来去不定,只有“人”这个概念是恒久的。亚里士多德则说,“张三李四”是具体的,我们看得见摸得着。而“人”这个概念,完全是我们看过了这么多具体的人,然后在脑子中产生的。所以真实存在的是具体的事物,不是概念。亚里士多德是柏拉图的学生,但是观点和柏拉图相悖,为此亚里士多德还说了一句名言:“吾爱吾师,吾更爱真理。”</p><h3 id="莱布尼兹与哲学乐观主义"><a href="#莱布尼兹与哲学乐观主义" class="headerlink" title="莱布尼兹与哲学乐观主义"></a>莱布尼兹与哲学乐观主义</h3><p>戈特弗里德·莱布尼茨(1646—1716)是数学家,在哲学上是一个理性主义者。他很快就接受了笛卡尔等人的学说,不仅发展出了属于自己的哲学体系,还和洛克展开了激烈的论战。洛克说,理性主义者们所谓的一些先于经验的公设啊,理念啊,和动物的本能没有区别。莱布尼茨针锋相对地反驳:人跟禽兽有什么区别吗?区别就是禽兽做事只凭经验,人却能根据经验总结出必然规律。禽兽不知道思考,总以为过去发生的事情,在以后相似的场合下还会发生。所以人可以利用禽兽的习性,去设计陷阱捕捉禽兽。</p><p>莱布尼茨是理性主义者,自然他也是使用先公设后推理的那套过程。莱布尼茨的公设是这样的:物质是占据空间的对吧?那么只要是能占据空间的东西,就可以被分成更小、更简单的东西。物质无限地分下去,最后剩下的,一定是不占据空间的“东西”——要是占据空间就能再分下去了。这“东西”不占据空间,所以它不是物质。所以它是精神。所以一切物质都是由精神组成的。莱布尼茨给这些不能再分了的、不占据空间的东西起名叫“单子”,他的理论也就被称为“单子论”。</p><p>但是因为理性主义者所有的结论都建立在不一定靠谱的公设上。只要公设、推理过程中有一点不可靠的东西,失之毫厘,谬之千里,整个体系就不知道扯到哪里去了。最后得出来的结论也就很难让人信服。</p><h3 id="牛顿与机械唯物主义"><a href="#牛顿与机械唯物主义" class="headerlink" title="牛顿与机械唯物主义"></a>牛顿与机械唯物主义</h3><p>牛顿最重要的成就是力学。他发现了万有引力定律,正确计算了天体运行的规律。他提出的力学定律在长达几个世纪的时间里都是不可撼动的。牛顿的第一身份是科学家,自然,在哲学上他倾向于经验主义。牛顿热衷于做实验,他的成就也都来自于实验,因此他有一句名言:“我不发明假说”。牛顿不仅在哲学上倾向于经验主义,在现实中还是洛克的好朋友。洛克赏识牛顿的才华,依靠他的社交关系提携过牛顿。一看洛克被莱布尼茨欺负了,牛顿二话没说,挽袖子就上,牛顿和莱布尼茨都是数学家。牛顿灭莱布尼茨,就灭在了微积分发明权这件事上。现代历史学家普遍认为,这两个人各自发明了微积分,所以微积分的基本定理叫“牛顿-莱布尼茨公式”,用两个人的名字合在一起表示的。但是莱布尼茨的论文比牛顿早了足足三年。但当时牛顿的声望、权势都比莱布尼茨大,再加上很多英国人出于民族主义心理支持牛顿,所以两个人在学术界大吵了一番。无非就是指责对方某年某月看过自己的笔记、某年某月我给你的通信中透露了我的微积分思想之类。不久,英国皇家学会经过详细认真的调查后庄严宣布——牛顿才是微积分的发明者,莱布尼茨是个大骗子。备注:此时的英国皇家学会会长就是牛顿。而且是他本人起草的这份调查报告。</p><p>顺便一说,这不是牛顿唯一的学霸行为。他不仅对外国学者狠,对自己的同事同样毫不留情,比如对罗伯特・胡克。胡克也是历史上有名的大科学家,制作过一些很牛的机械,还发明了“胡克定律”以及“细胞”一词。牛顿能在和他人的斗争中节节胜利,归根结底,靠的是他在力学上的伟大成就。别人对牛顿什么都能质疑,唯有在学术上,谁也打不过他。但就算是牛顿这么强的对手,莱布尼茨仍旧给了他一次反击。牛顿最伟大的成就是发现万有引力。但牛顿还有一个问题,就是没能说明相隔万里的星球之间到底是怎么产生引力的。连牛顿本人都不相信,相隔这么远的星球在没有任何媒介的情况下还能发生力的作用,他说:“在我看来,这种思想荒唐至极。”这也不能怪牛顿,因为直到后来爱因斯坦发表了相对论原理,对引力才有了令人满意的解释。而在当时,聪明的莱布尼茨立刻发现了引力是一个问题,他攻击牛顿说,你如果不能解释物体之间到底通过什么媒介产生了引力,那么你这理论就是一番空话。</p><p>牛顿能给哲学留下影响,不是因为他进行了什么哲学研究,而是他在物理学上的成就实在太大,余波就把哲学给影响了。这个成就就是他的力学。用物理学来解释包括人类意识在内的整个世界,这种观点叫作“机械论”。机械论就是除掉了辩证法之后的唯物主义,也可以叫作“机械唯物主义”。就像经验主义者集中在英国、牛顿是英国人一样,机械论的急先锋也是一个英国人,他叫作霍布斯。</p><p>托马斯·霍布斯(1588—1679),虽然霍布斯也信仰基督教,但是他的著作《利维坦》实际上宣扬的是无神论思想。霍布斯拿铃声来说明他的机械论观点。他说,在“铃铛颤动直到我们听到声音”这个事件过程里,铃铛只有运动没有铃声,空气只有运动没有铃声,传到我们的耳朵里了就产生了铃声。所以真正存在的是运动,不是声音。</p><p>17、18 世纪的人们崇拜牛顿的学说,那时的机械论也被认为有着伟大的前途。机械论者希望,有一天在医学、心理学、伦理学、政治哲学等领域,都可以应用牛顿力学,或者像牛顿力学那样用几个简单的数学公式去解释。虽然人们可以通过实验证明物质能对意识产生严重的影响(比如脑萎缩会降低人的思考能力,打一棍子可以让人立刻昏厥,喝酒、嗑药可以让人产生幻觉),可以证明人类不能靠思想意念去改变物质,但是也仍旧不能严格证明意志完全由物质决定。</p><h3 id="决定论"><a href="#决定论" class="headerlink" title="决定论"></a>决定论</h3><p>机械论虽然可以条理清晰地解释这个世界,但是按照机械论的说法,人类不过是这个世界中可有可无的一件事物而已,和桌子板凳、花鸟鱼虫没有本质的区别。我们的意识不过是一系列物质作用的结果,随时可以消失,毫无永存的希望,更谈不上还有什么人生意义。就像世间的其他事物一样,存在就存在了,消失就消失了。这很容易推导出虚无主义和享乐主义。以及最可怕的决定论.</p><p>决定论的意思很简单,既然世间万物都可以用物理规律来解释,那么每一个事件之间必然要遵循严格的因果关系。如果人的意识是完全由物质决定的,那肯定也得服从严格的物理定律。那么,整个世界该如何发展,该走向何处,都是由自然定律决定好了的。就像人们根据力学可以预测星辰位置一样,人们也可以根据自然规律来预测未来所有的事件。</p><p>一个支持决定论的证据是,在 20 世纪之前,人们认为世界上不存在真正的随机数。没有随机,那就意味着一切都可以计算。数学家拉普拉斯曾说,只要他拥有足够多的数据,他就可以按照机械定律推出未来世界的全部面貌。这就像某些科幻小说里设想的那样,假如有一台超级计算机,就可以计算出未来的一切。可怕的地方就在于,一旦我们接受了最严格的决定论,那就意味着人类没有了自由意志。因为我们的意识是由组成我们身体的物质决定的。组成我们身体的物质又是由物理定律决定的。所以,我们头脑中的每一个念头,其实都是在几万亿年前的宇宙大爆炸的那一瞬间就被决定好了的。且不说这想法很诡异,关键是,那人生还有什么意义啊?假如人的全部意识都是事先被决定好的,人就没有自由,那不就没有道德可言了吗?人就不需要为自己的行为负责了呀。因而,从决定论——特别是从严格的决定论所导出的结论,是荒谬甚至恐怖的。如果按照决定论的观点生活,人类的社会秩序将会荡然无存,人类的一切工作都会变得没有意义,一切罪行都可以得到饶恕。这世界显然不是任何一个哲学家想要的。除了建立在机械论上的严格的决定论之外,在哲学史上更流行的是部分决定论,也就是说物理世界是被决定的,但是人有自由意志。这当然更容易让人接受。</p><p>古希腊的斯多葛学派就相信部分决定论。他们认为我们不能控制事物,但是可以控制我们自己对待生活的方式。所以这个学派提倡随遇而安的生活态度。沉思录的作者马可·奥勒留就收到了斯多葛学派的影响.</p><h3 id="休谟与人性论"><a href="#休谟与人性论" class="headerlink" title="休谟与人性论"></a>休谟与人性论</h3><p>挑战机械论和决定论的人,乃至挑战整个科学体系的人就是休谟.</p><p>他 23 岁完成了人性论,休谟是英国人,也是经验主义者。但休谟认为他之前的经验主义者和理性主义者都有根本缺陷。休谟认为,你们讨论“何事真实存在”之类的问题,实际上这些问题人类根本没有能力回答,所以你们才能怎么说怎么都有理,正反两面的观点都能成立。在休谟这里,经验就是人的感觉印象。我感觉到了什么就是什么,至于这感觉从哪儿来的,是真是假,我不知道。休谟认为,我们所谓的“我”,不过是一堆经验片段的集合而已,并没有一个独立于经验的、实在的“我”存在。</p><p>笛卡尔认为“我”是超越了客观世界的真实存在,在休谟看来,“我”不过是后天学习到的一堆经验片段而已。真正有没有“我”呢?不知道!不知道就不知道,没关系。我们能得到的经验就是眼前的生活,在没有明确的证据证明面前的生活都是幻觉之前,我们就照着自己平时的经验正常生活下去就可以了。我们没必要也没能力去无限地怀疑世界。反正想也想不出结果来,就别想了.</p><p>休谟打算用怀疑论来抛掉前人所有不可信的经验。休谟想,有什么知识是切实可信的呢?他找到两种。第一种是不依赖于经验的知识。比如几何学,它自身是不矛盾的,完全符合逻辑规则,而且不依赖经验存在。第二种可靠的知识是我们自己感受到的经验,摸到什么、看到什么,这些都是可信的(当然,还是那句话,这经验是不是来自于幻觉我们先不管)。第一种是纯粹的演绎推理,第二种是经验归纳.那么,因果律属于第一类知识吗?我们能不依赖于经验,只靠逻辑推导出因果律吗?显然不能。那么,因果律可以靠经验总结出来吗?休谟说,不能,因为你就算之前多次看到苹果离开树枝落到地上这个现象,你也不能保证,下一次苹果还一定会落到地上。“必然”这个东西不在我们的经验范围之内。我们之所以认为这里有“必然”性,是因为我们过去无数次地看见了这两件事连在一起发生,所以我们就想当然地认为,这两件事之间有必然的联系,在未来也会永远连在一起发生。</p><p>休谟指出,人相信因果律其实是一种心理错觉,只因为我们发现两件事总在一起发生,我们就期待它们能再次一起发生。但这其中并没有可靠的根据。举个简单的例子,假如有一个没有科学知识的原始人,他通过观察发现,公鸡打鸣之后,总伴随着太阳升起,没有一天例外。那么他会认为,公鸡打鸣是太阳升起的原因。这显然是错的。同理,农夫喂鸡也是这个道理.因为再多次的偶然累计在一起也不可能把偶然变成必然。</p><h3 id="康德与理性批判"><a href="#康德与理性批判" class="headerlink" title="康德与理性批判"></a>康德与理性批判</h3><p>我们说哲学的一切都是从怀疑开始的。</p><p>近代哲学从笛卡尔的怀疑开始,这个怀疑让人们踌躇满志,觉得有一个广阔的空间可以施展拳脚。然而一路怀疑下去,到了休谟的怀疑,把人类所有的知识都怀疑没了,只剩下荒诞。这个时候有两个会严重摧毁生活的哲学观点。一个是休谟的怀疑论,一个是科学的决定论。可怕的是,这两个观点正好是互相矛盾的两个极端。反对一个就等于拥护另一个,采取中庸之道的那些结论,更像是诡辩论而不是严谨的推理。</p><p>接下来是属于德国的时代。康德终于来了。包括康德,以及后面的谢林、黑格尔、费尔巴哈、叔本华、尼采、马克思、胡塞尔、海德格尔,还有对哲学影响颇大的爱因斯坦、海森堡。这个超豪华阵容全部都是德意志人。1781 年,康德的杰作《纯粹理性批判》出版了,由此打破了沉寂。</p><p>我们先集中关心一下他到底是怎么解决决定论和休谟怀疑论这两个大问题的吧。康德说,当年大家都以为地心说正确,可是天文学家根据地心说怎么也计算不出正确的结果。哥白尼大胆地把地心说掉过来,改成日心说,一下子解决了问题。那过去的哲学家呢,都认为我们的认识要符合客观世界,但是讨论了半天都没有结果。康德认为,我们应该把主客观世界的关系颠倒过来!在康德的哲学世界里,所有的知识都要先经过人类心灵的加工,才能被人类认识。在他的哲学里,不是心灵去感受经验,而是心灵加工经验,心灵生产了经验。有一个最常用的比喻,有色眼镜。这个比喻说,假设每个人终身都必须戴着一副蓝色的有色眼镜。这个世界上所有的事物,必须都通过有色眼镜的过滤才能被人看到。那么所有人看到的就是一个蓝色的世界,而世界真实的面貌是人永远看不到的。在这个比喻里,有色眼镜是先天认识形式,事物原本的颜色是物自体,人类看到的蓝色的世界,是表象。康德认为,这世界(物自体)是人类永远无法真正认识的,人类看到的只是表象的世界。但是由于每个人对真实世界的表象方式(先天认识形式)都是相同的,所以人类看到的同一个东西的感受还是一样的,因此我们察觉不到真实的事物是否被扭曲了。所以这个世界观并不和我们的生活经验相悖。</p><p>康德之前的哲学危机,是休谟对因果律,乃至对人类理性能力的怀疑。康德的解决方法是,他把世界分成了两个部分。一个部分完全不可知,另一个部分则可以用理性把握。不可知的那部分因为永远不可知,所以对我们的生活没有什么影响。只要我们在可把握的世界里生活,理性就又恢复了威力。今天我们在谈论哲学的时候,无论讨论多么时髦、多么先锋的理论,都无法绕过康德。用叔本华的话说,任何人在读懂康德之前都只是一个孩子。按照学术史的发展规律,面对康德这么一座高峰,后来人要常年生活在他的阴影之下,只能做一些修修补补的工作。事实上这工作有人做了。他们就是费希特和谢林。</p><h3 id="黑格尔与辩证法"><a href="#黑格尔与辩证法" class="headerlink" title="黑格尔与辩证法"></a>黑格尔与辩证法</h3><p>存在即合理。- 格奥尔格·黑格尔(1770—1831).</p><p>就在法国大革命这一年里,黑格尔开始阅读康德的作品。过去的哲学家们,也就是形而上学家们,他们认为真理是固定不变的,静等着人类去发现。说白了,这些形而上学家们都觉得这个世界上存在一个叫“真理”的固定的东西,就跟事先写好的考试答案一样,等我们去找到它。因此我们的教科书才说形而上学是孤立、静止地看待世界。黑格尔认为,这么想就太幼稚了。我们今天对辩证法有一种庸俗的理解,说辩证法就是“看待事物要分两个方面”。别人批评一个现象,你非要说“要辩证地看这件事,这件事也有好的一面嘛”。这是对辩证法的极大误读。这不叫辩证法,这叫诡辩法,它的唯一作用是把所有的事实都捣成一片糨糊,逃避一切有意义的结论。</p><p>这当然不是黑格尔的辩证法。黑格尔的辩证法是什么意思呢?</p><p>传统的逻辑,也就是我们一般人能接受的逻辑,都要遵守“矛盾律”。“矛盾律”的意思是,一件事不能自相矛盾,事物和事物之间也不能互相矛盾。“我长得漂亮”和“我长得丑”两者只能有一个为真,不可能同时为真。”黑格尔认为:“矛盾就是世界的本质。矛盾的双方可以共存,但是处在互为差异、甚至互相冲突的动态之中。事物的正题和反题会发生强烈的冲突,这个冲突的结果并不是一方消灭另一方,而是正题和反题最终化为“合题”达到了协调,升华了。新的合题产生之后,它的反题也随之产生,这样就又产生了新的矛盾,又要有新的冲突和升华,再产生新的合题。变化到最后是什么呢?是黑格尔的终极真理,黑格尔给它起个名字,叫做“绝对精神”。黑格尔说,之前的哲学家错就错在认为人的理性世界和客观世界是对立的、矛盾的两个事物。理性经过不断的辩证,就可以完全符合客观世界的真实面貌。理性就是世界的本质,世界的本质就是理性。所以说,宇宙的本质是精神,而且是一种理性精神。这个理性精神,就是黑格尔的“绝对精神”。</p><p>因为重视历史过程,黑格尔是第一个重视研究哲学史的人。黑格尔的历史观后来被马克思批判性地继承,变成了辩证唯物主义历史观——马克思也认为,历史的进程是有方向的,不可逆转、不可阻止的,但是可以预测的。马克思预测历史通向的是共产主义,黑格尔的历史通向绝对精神。</p><h3 id="叔本华和生命意志"><a href="#叔本华和生命意志" class="headerlink" title="叔本华和生命意志"></a>叔本华和生命意志</h3><p>黑格尔是形而上学的巅峰,他创造了一个史上最完备、最庞大的形而上学世界。然而,这时候突然有一个人站出来对黑格尔破口大骂,而且骂得超级狠。他说黑格尔是“一个平庸、无知、愚蠢、令人讨厌恶心的江湖骗子”。这人叫叔本华,他崇拜康德并且鄙视黑格尔。</p><p>到了 30 岁的时候,叔本华最重要的著作《作为意志和表象的世界》出版了。又过了一年,叔本华得到了一个难得的机会,他被柏林大学聘用(没薪水)。叔本华终于可以和自己的宿敌黑格尔当面对决了,于是他要求校方把自己的课和黑格尔的课安排在同一个时间。那时候黑格尔的学生不算多,一堂课也就只有三百个人。那么叔本华的课上有多少人呢?基本不超过五个。只干了半年叔本华就崩溃辞职了。</p><p>56 岁的时候,《作为意志和表象的世界》第二版出了,结果只卖了不到三百本。等到他 63 岁的时候,他出了一本《附录与补遗》。这本书以格言体写成——仿佛我们今天的人生小感悟.不过,还是人生小感悟的力量比较大。过了一段时间,这本《附录与补遗》终于让人们接受了叔本华。</p><p>叔本华骂黑格尔,自然也不会喜欢黑格尔的哲学。叔本华是康德思想的继承者。前面讲康德时那个“蓝色眼镜”的例子,可能会让很多人认为物自体是有很多个的,而且和表现出的事物一一对应。比如桌子有一个它对应的物自体,“我”也有一个“我”对应的物自体。叔本华说,这是不对的。<br>因为我们在区别两个事物的时候,离不开空间概念。比如两个东西形状不同,摆放的位置不同,等等。可是物自体不具备空间属性啊,所以我们不可能把物自体区分成一个一个不同的样子。叔本华认为,万物的物自体是统一的,只有一个。这个物自体,叔本华给它起了个名字,叫作“生命意志”。</p><p>那么生命意志是个什么东西呢?</p><p>简单地说,是一股永不停歇的力量。这股力量驱使着万物去运动,去发展。比如人和动物的食欲性欲,比如植物破土而出的欲望。举例子说,我们以为自己生活、恋爱、结婚、工作是根据我们的理性选择的。而叔本华认为,真正驱动你的都是种种欲望:生殖的欲望、享乐的欲望、征服的欲望,等等。你以为你在靠理性生活,实际上躲在理性背后的是生命意志,生命意志在驱动你作出种种选择。</p><p>说到这里,你可能会有疑惑:叔本华的理论有什么意思呢?他不就是把康德的物自体给解释成生命意志了吗?这和康德有多大差别呢?</p><p>就是这一点点小差别,导致叔本华和康德的世界观、人生观相差十万八千里。我们还记得,康德的形而上学,目的是干掉休谟的怀疑论。休谟说我们的理性根本无法认识这个世界的真相。康德点点头:休谟说得没有错,我们的确无法认识物自体,可是我们生活在表象的世界里。在这个表象世界里,一切都先经过先天认识形式的加工。而先天认识形式我又用理性给分析得清清楚楚了,那表象世界还是被理性统治的呀。所以在康德看来,理性就是我们这个世界的统治者。没错,理性确实管不了物自体,但是物自体也不影响我们的世界呀。叔本华说,不,物自体能影响我们的世界。不仅能影响,而且影响力超大,我们用理智控制不了。在康德那里,这个世界的基础是井井有条的理性。在叔本华这里,这个世界的基础是无法控制的生命意志。</p><p>因此康德对世界的看法是乐观的。叔本华对世界的看法是悲观的。叔本华认为,欲望本质上是痛苦之源。因为满足不了欲望,人会痛苦。满足了欲望,人又会产生新的、更高的欲望,还是会痛苦。如果人满足了全部的欲望,而且没产生新的欲望,人会幸福吗?不会,人会感到空虚和无聊,这也是痛苦。所以快乐只是暂时的,痛苦才是永恒的。人生就好像在痛苦和无聊之间不停摆动的钟摆。叔本华认为,应当增强自己克制欲望的意志力。我们可以提高自己对这世界的认识(当然是去认识叔本华所理解的那个世界),增强我们的理性,用理性抑制和控制感性冲动。然后,把自己的感情和欲望上升为全人类的感情和欲望,这样就可以尽量消除个人的欲望。接下来,我们要强迫自己不去做想做的事,反而去做不想做的事,抛弃一切现实的理想。像苦行僧一样地修行,通过苦行来抑制生命意志。不反对别人损害自己,欣然接受任何损失,把这当作考验自己战胜生命意志的机会。最终欣然接受死亡。当我们实现了这一目的后,就可以达到物我两忘的境界,就算降服生命意志了。除了苦修禁欲之外,叔本华还给出了另一个方法:欣赏艺术。他认为,人在欣赏真正的艺术的时候,内心是非功利、不带欲望的,也就脱离了生命意志的控制。因为最伟大的艺术家都是在努力审视自己的内心,表达自己内心深处最真实的感受。而且艺术是感性的、非理性的。这就是说,最伟大的艺术品反映的是“生命意志”的真相。在所有的艺术中,叔本华最推崇音乐。</p><p>在宗教方面,叔本华认为,基督教教义中的原罪就是生命意志,基督教鼓励的赎罪精神就是要求人们去克服生命意志。所以基督教比其他宗教在欧洲更受欢迎,因为它认识到了生命意志的悲观主义精神,而其他宗教都是乐观主义的。佛教比基督教更重视禁欲。基督教在某种程度上并不禁欲,比如鼓励人多生养,鼓励人勤奋工作。佛教不同,佛教认为欲望是痛苦的来源,主张彻底摒弃一切欲望。这和叔本华的观点很像。这不是巧合,叔本华的哲学观点深受印度佛教的影响。据说他的书桌上经常摆放的是一尊康德像和一尊佛像。</p><h3 id="尼采和权力意志"><a href="#尼采和权力意志" class="headerlink" title="尼采和权力意志"></a>尼采和权力意志</h3><p>对于哲学史来说,叔本华最大的价值不在于悲观主义的世界观,而在于他暗示了一个巨大的危机。理性没落的危机。</p><p>之前的哲学家们为什么都要坚持理性呢?理由很简单,理性是知识最好的载体。哲学是求“真”的。这个“真”是一个逻辑判断,是一个理性概念,离开了理性,就无所谓“真”不“真”。光谈“感觉很棒”,那样无法经受苏格拉底的怀疑,那我们也就没有必要再研究哲学了,当叔本华认为生命意志高过了理性的时候,他等于告诉人类,理性的能力实在太小了,理性既不能揭露世界的本质,也难以对抗本能的欲望。理性这东西实在没用。</p><p>叔本华去世五年后,他的思想随着他的作品遍布欧洲大陆。在德国莱比锡市的一家旧书店里,一个青年鬼使神差地拿起了一本《作为意志和表象的世界》。这个年轻人就是尼采。这时他才 21 岁.毫无疑问,在哲学上,尼采是站在叔本华那边儿的。尼采是一个充满激情的人,他喜欢音乐,喜欢爬山,崇拜音乐家瓦格纳,他大喊“上帝死了”,还自比是太阳.但富于激情的尼采基本上度过的是悲剧的一生。他一生不被人理解,著作无人问津。他最重要的著作《查拉图斯特拉如是说》印完之后,别说卖了,送也只送出去七本。在他写作生涯的最后三年,他前后花了五百个银币出版自己的著作,没拿到半分稿费。讽刺的是,在尼采疯了以后,财富和荣誉接踵而来。他出了名,人们像对待圣人一样崇拜他,王公贵族争相拜见,就好像只要看一眼这位已经失去了神志的可怜人,就可以沾上一点哲人的仙气一样。</p><p>在尼采发疯以后,他妹妹利用整理尼采著作的权利,任意增删、篡改尼采的作品及信函,编成了适合她自己口味的《权力意志》。因为她还撰写和尼采有关的著作,她还两次被提名诺贝尔文学奖。尼采的妹妹把尼采的学说变成了种族主义、国家主义的武器,最终成了纳粹主义的一部分.因为尼采妹妹的这些行为,尼采的学说成了法西斯国家官方鼓吹的哲学家。第二次世界大战结束,尼采的名声也随之一落千丈,直到后来才慢慢恢复。</p><p>尼采继承了叔本华的形而上学。叔本华说物自体是“生命意志”,尼采给改造成了“权力意志”。“权力意志”一词中的“权力”容易引起误解。这并不是政治权力的意思,而是指要让自己变得更强大、更强壮、更富创造力的欲望。尼采把人分成了强者和弱者。强者体现了权力意志,他们的特征是积极向上、勇于进取、勇于牺牲、善于创造。弱者相反,特点是胆小、保守、善妒、虚伪。他认为,同情弱者没错。但弱者不能以此为理由,去要挟、榨取强者,去拖强者的后腿,这样做是可耻的。他谈的第一种道德是属于弱者的道德,尼采叫它奴隶道德(又叫“畜群道德”)。表面的内容是同情、仁慈、谦卑、平等。其实本质上,是弱者为了掩盖自己对强者的恐惧、嫉妒和自私,借助奴隶道德去限制强者。弱者对强者感到恐惧,因此奴隶道德强调“仁慈”“谦卑”,把强者和特立独行的人看作是危险人物,要求社会限制他们的能力。尼采说的第二种道德是强者的道德,它可以叫作贵族道德。这种道德鼓励人们积极进取,特立独行,崇尚强大,鄙视软弱,追求创新,拒绝平庸,它代表了生命积极的一面。尼采认为,奴隶道德和贵族道德最明显的区别在于:奴隶道德总是在禁止,不许人们做这做那;贵族道德则是在鼓励人们自由创造。</p><p>尼采的道德观不是会造成弱肉强食吗,不是会造成强者欺凌弱者吗?尼采的回答是,人的本性就是残忍的。尼采的道德观和基督教道德有明显的矛盾。在尼采生活的社会里,基督教道法就和咱们这里的儒教道法一样,是全社会广为接受的道德规范。《圣经》里说什么事情是善的,那全社会的人都不用多想,都认为这件事是善的。但尼采认为,基督教道德是典型的奴隶道德,本质是伪善的。基督教鼓励人们变得谦卑,其实就是鼓励人们做弱者。所以尼采大喊“上帝死了!”意思是,他想去掉上帝。如果没了上帝,人们也就不需要无条件地遵守基督教道德了。</p><p>尼采发现,大部分强者都被奴隶道德压抑着,不能摆脱弱者对他们的束缚。因此,尼采希望“超人”出现。“超人”这个词在尼采的理论里不是指拥有强大权力的人,而是指能够完全按照自己的意志行动、能充分发挥自己的创造力,并且能摆脱奴隶道德、不被弱者束缚的强者,简而言之,尼采推崇的是一种精英主义。</p><h3 id="进化论对哲学的影响"><a href="#进化论对哲学的影响" class="headerlink" title="进化论对哲学的影响"></a>进化论对哲学的影响</h3><p>科学是坚持纯理性的。科学使用的是归纳法和演绎推理。所有的科学理论,都必须用理性的文字表达,都必须经得住严谨的实验。科学创造的奇迹,就是理性创造的奇迹。形而上学必须使用理性工具——否则无法进行苏格拉底式的怀疑。那么,形而上学的溃败,其实就只是理性的溃败。理性工具不好用了嘛。然而与此同时,科学正在用一个接一个的奇迹来捍卫理性的尊严。物理学,进化论和心理学的发展让人们对科学的自信心变得出奇的高。人们相信,只要假以时日,科学可以解决一切问题。就算是艺术、哲学那些过去被认为科学难以碰触的领域,将来运用心理学也可以解释了。</p><p>科学的发展给哲学带来了两个影响:第一个影响是把宗教完全打趴下了。第二个影响是,随着科学的触角越来越广,机械论和决定论必然重新抬头。唯物主义出现了升级版辩证唯物主义。不过在讲这个我们很熟悉的理论之前,我们必须先讲一讲进化论。它对于重新认识科学和哲学,乃至我们自己,都有很大的意义。</p><p>进化论的关键内容有这么几条:第一,生物的基因信息可以遗传给下一代:第二,在遗传的时候,基因会发生不可控制的随机变异;第三,整个生物种群都面临着巨大的生存压力,每一代新生物的数量却大于自然资源能够供养的数量,因此每一代新生物中的大部分都会死掉。第四,生物后天的变化在大部分情况下不能改变基因。生物进化的过程是这样子的:因为每次遗传都会产生一系列变异,因此每一代新生物都会有一些个体的生理特征不同于父母辈。或者说,总有些个体长得“怪”。又因为生存压力特别大,每一代里的大部分都会死掉,因此假如这些长得“怪”的地方正好能适应当时的环境,那么拥有这些“怪”基因的生物就有更大的概率存活下来,这些“怪”基因也会保留下来,从而成为这个生物基因中的一部分,生物就完成了一次微小的“进化”。进化论仅仅阐述了一套基因变化的规律,这中间并没有任何道德含义。而且正是进化论把神创说从生物界赶走了,才把生物学中的道德元素降到最低的程度。</p><p>进化论对哲学有什么影响呢?</p><p>第一个影响,是严重打击了基督教的权威。第二个影响,是进一步消除了人类的神圣性,并且可以根据进化论产生一些哲学推论.推论一,叔本华说生物有生命意志,其中生殖是最重要的。生物的一切行为都是为了生殖。这个观点正好符合进化论。进化论也认为,为了能将基因保留下来,一切都应该以生殖为目的。推论二,尼采说权力意志,说每个人都想成为强者,这也在一定程度上符合进化论。 因为生物在进化中必须变得越来越强,才能不断增强种族延续的可能。</p><h3 id="相对论与量子力学对哲学的影响"><a href="#相对论与量子力学对哲学的影响" class="headerlink" title="相对论与量子力学对哲学的影响"></a>相对论与量子力学对哲学的影响</h3><p>我们来简单谈一下爱因斯坦的相对论.</p><p>首先说狭义相对论,我们看看两个最直观的结论。第一,光速是永恒不变的。我们在前进的自行车上打手电筒发出的光速,和我们站着不动打手电筒的光速一样。任何物体的移动速度都不能超过光速。第二,说一个宇宙飞船接近光速,飞船之外的人去看这个飞船,会发现飞船的时间变慢了,长度也缩短了。然而飞船内部的人却没有感觉。准确点说是这样,相对论说的是,两个运动状态不同的观测者,在看同一个物体的时候,他们看到的这个物体的时间、长短、质量都是不同的。在牛顿时代(也是咱们普通人的概念),时间和空间都是独立的,互相没有关系。就像“5 分钟”和“3 厘米”根本没法放在一起计算一样。但是狭义相对论认为,时间和空间不是互相独立的,可以互相影响,不同运动状态的人观察同一个物体,观测到的时间、大小都不相同。因此时间和空间得放在一起研究,统称为时空。质量和能量也不是互相独立的,统称为质能。这也是核武器的理论基础。</p><p>而广义相对论,它解释的是万有引力。在相对论之前人们已经知道了万有引力的存在,但是不知道引力是如何产生的。万有引力能够让两个星球相隔万里还有相互作用“力,这点连牛顿都不太说得明白。广义相对论的意思是说,当空间中存在物质和能量的时候,空间就会受到影响而弯曲,质能越大,空间弯曲得越厉害。引力就是这种空间弯曲产生的。有一个非常形象的比喻。好比我们的空间是一张抻平的床单,当我们往上放一个木球的时候,床单会被压下去,那么木球周围其他更轻的小球就会滚向木球,看上去就好像小球被木球吸引了一样。假如放的是铅球呢,床单会被压得更严重,造成的空间扭曲更大,引力也就更大。</p><p>相对论对于哲学的意义在于,这进一步打击了人们对先验理性的信心。当年的理性主义者、形而上学家们自信满满地追求绝对真理、先验理性,此时看来,他们自信的真理就不一定是绝对的了。相对论还有一个衍生的结论:我们对整个宇宙的认识有很大的局限性。</p><p>不止相对论,物理学家们在研究量子的时候发现了一个奇怪的现象。物理学家观测一个电子,越是精确地确定其位置,就越无法确定它的动量;越是想更精确地测定它的动量,就越测量不到它的位置。这并不是因为科学家的观测技术不行,而是由严格的理论决定的。这个规律叫作“海森堡测不准原理”或者“海森堡不确定性原理”。类似的怪事,还有电子的“波粒二象性”。从传统意义上说,电子不可能既是波又是粒子。然而科学家在实验中发现,电子既能显示波的特性,又能显示粒子的特性,关键看科学家们用什么方法去检测它。用一种方式观测就是波,用另一种方式观测就成了粒子了。科学家们对于一个电子的运动状态只能预测出一个概率,只能说大约、可能在哪儿。物理学成了一门缺乏确定性的学说。爱因斯坦有一句名言:“上帝不掷骰子。”意思是说,世界不可能真正是随机的,一切都是确定的。然而,这回是爱因斯坦错了。这意味着,人类对世界的认识能力又受到了进一步的限制,而且只要量子力学不被推翻,这限制就永远无法超越。</p><p>但量子力学彻底打败机械论和决定论。这对于坚持人有自由意志的学者来说是一件好事。</p><h3 id="罗素与逻辑实证主义"><a href="#罗素与逻辑实证主义" class="headerlink" title="罗素与逻辑实证主义"></a>罗素与逻辑实证主义</h3><p>时间进入 20 世纪,理性还没有被打倒,还有很多哲学家在为理性的尊严而奋斗。这次冲到前面的,是罗素老师。罗素高寿,活了 98 岁。他出生的时候是中国同治十一年,颐和园还没开始建造。他去世的时候是 1970 年,互联网已经诞生了。</p><p>罗素和怀特海花了十年时间,合写过一本数学原理.虽然销量不好,但是罗素在写作《数学原理》的时候受到了启发。他想,既然能用逻辑来建立整个数学大厦,那么,是不是也能用类似的方法建立整个哲学大厦呢?严谨的哲学研究要靠理性思维。那么,什么样的语言能够最严谨地表达理性思维呢?那就是严格符合逻辑的语言。更准确地说,是逻辑符号。就好比我们在数学题中写的那些数学符号,它们是最严谨、最符合逻辑的。</p><p>逻辑实证主义者发现,过去的哲学家们不是很重视语言的严谨性,他们经常使用大白话来表达自己的哲学思想。哲学家们常自己提出一些特定的术语——就像我们前面说过的“生命意志”“权力意志”——他们又不给这些术语下特别严谨的定义,只是用粗浅的大白话解释一番,很容易引起读者的误解。“逻辑强调的是演绎推理,即从一个真的前提,推理出一个真的结论。可是,光有演绎推理的话,那得出的只能是重复的命题,得不出新的知识来。要扩展知识,我们要从经验中吸收。“实证”的意思就是说,逻辑实证主义得出的新结论,必须能有经验实实在在地证明。</p><p>逻辑实证主义的理想很好,要坚持绝对的理性、绝对的正确,可是最后发现,这个绝对的理性却得不到任何有意义的结论,连一个普遍的理论都得不出来。</p><h3 id="实用主义"><a href="#实用主义" class="headerlink" title="实用主义"></a>实用主义</h3><p>实用主义和逻辑实证主义基本上是同一个时期的学说。他们遇到了和逻辑实证主义一样的问题:科学在飞速发展,然而哲学却一直在原地打转,这问题到底出在哪里?</p><p>实用主义和逻辑实证主义的思路不一样,逻辑实证主义看到的是科学的严谨性,希望哲学也能和科学一样严谨。实用主义则看重科学的实用性,看到科学家没哲学家那么多废话,在科学研究中什么理论好用就相信什么。实用主义者觉得,哲学也得像科学这样,不再说空话,不再讨论空泛的大问题,而是重视哲学的实用性。</p><p>实用主义在美国很受欢迎,实用主义哲学家也大多是美国人。有人说,这是因为实用主义正好契合了美国人的务实精神——这是好听的说法,难听的说法是美国人世俗功利。比如美国的司法采用判例法。意思是,过去类似的案子是怎么判的,这回的案子就参考着判。或许有人认为这过于儿戏了,难道国家制定的法律不是最大的吗?但判例法认为,一次性制订的司法是很难完善的。那么我们就通过每一次的审判,来不断纠正、完善国家的法律。你看,这不正好和实用主义者的真理观吻合吗?一些有社会主义倾向的政党在西方国家兴起。他们不搞武装革命,也不想消除阶级差别,而是搞工会,搞社会福利。不像马克思那样试图从根本上解决问题,而是从小地方一点一点改良,遇到什么问题就解决什么问题。比如资本家对工人压榨得太厉害了,国家就制定法律保护工人。垄断企业影响市场竞争,就制定反垄断法律限制垄断企业。工人购买力下降,就设置最低工资,增加社会福利。实际上,马克思当年为了维护工人阶级利益提出的很多要求,大部分都被资本主义国家接受并且实现了。如今这种改良式的资本主义在西方颇受欢迎.</p><h3 id="波普尔和证伪主义"><a href="#波普尔和证伪主义" class="headerlink" title="波普尔和证伪主义"></a>波普尔和证伪主义</h3><p>我们大都了解一点弗洛伊德对梦的分析,这里的问题是,无论患者梦见了什么,医生都会进行解释,都会说这符合弗洛伊德的理论。换句话说,无论患者出现任何情况,弗洛伊德都不可能是错的。这样的理论,的确不会和现实产生任何矛盾,但是,能说它是现实的真实反映吗?</p><p>波普尔看出了其中的问题,提出了一个检验科学理论的重要标准:证伪。什么是科学理论,什么不是?其中关键的标准,是看这个理论有没有可以被证伪的可能。具体来说:科学理论必须能提出一个可供证伪的事实,假如这个事实一经验证,便承认该理论是错的。如果暂时没有人能证明它是错的,那它暂时就是真的。换句话说,所有的科学理论都是一种假说,科学家没有办法证实任何一种科学理论。但是科学理论可以给别人提供验错的机会。在没被检验出错误之前,我们就姑且相信这个科学理论是正确的。“在现实生活里,这个标准可以很方便地把巫术、迷信和科学区分开。</p><p>证伪主义对社会哲学也有影响。现在,世界大部分国家的刑事司法都接受“无罪推定”原则。意思是说,假如没有足够的证据证明一个人是犯罪嫌疑人,那么就应认为他是无罪的。</p><p>波普尔还根据证伪主义提出了自己的政治观。</p><p>有一种社会观念,认为历史的发展轨迹是必然的,这种观念叫作“历史主义”,黑格尔和马克思都持这样的观点。波普尔不同意这样的看法。没有永恒不变的真理,所有的理论都可能是错的。所以,也就不存在什么“历史的必然规律”。而且科学理论未来的发展方向也是难以预测的。就比如在牛顿时代,没人能够预测相对论的出现,也没人能预测牛顿理论将会在哪里出问题。因此,预测未来的历史规律,一劳永逸地设计一种绝对正确的政治制度,也是不可能的。波普尔因此主张应当建立“开放社会”,要求执政者能够广泛接受意见,赋予大众质疑政策的权利。因为执政理论和科学理论一样,永远都可能是错的。必须要不断地接受证伪,才能保证理论的正确。证伪主义的政治观,最关心的不是谁制定的政策,而是无论谁制定的政策,都不能成为绝对真理。不管是美国总统下的命令还是全世界人民投票的结果,都要给别人留出修改、推翻它的机会。</p><p>那证伪主义是怎么终结形而上学的呢?演绎推理的规则是,从一个绝对为真的命题出发,推出一个绝对为真的结论。只有这样,才能保证每一个结论都是正确的。但是证伪主义反对的,恰恰就是绝对为真的命题——因为绝对为真的命题是不可证伪的!是毫无意义的,它和巫术、宗教理论都是处于一个地位,毫无讨论的必要.</p><h3 id="人生的意义"><a href="#人生的意义" class="headerlink" title="人生的意义"></a>人生的意义</h3><p>形而上学走不通,形而上学的问题都没有答案。</p><p>形而上学的任务,是用理性思维去研究世界本质等“大问题”。形而上学走不通,也就是说,理性不可能回答世界的本质是什么,有没有终极真理,终极真理是什么,人生的意义是什么等大问题。实际上,所有的形而上学都会陷入无法证明自身的困境。既然形而上学的问题都没有答案,那么就意味着我们不知道人类的一切知识是否可靠,这个世界就没有了终极真理,没有了本质,终极问题没有答案,最聪明的人们追求到最后,不约而同地发现这是一条绝路。</p><p>那么接下来该怎么办呢?该怎么回答“人生意义是什么”的问题呢?</p><p>首先说一下我对这个世界的看法:这个世界的本质是什么呢?什么知识是真实可信的呢?对于客观经验领域,也就是对于我们能看得见、摸得着的物质世界,最好的研究方法是“基于经验主义和实用主义的、可证伪的理论”。说白了,就是科学。作为现代人,拒斥科学方法和科学成果是不可能的。</p><p>对于“世界的本质到底是什么”的问题,这里没有标准答案,愿意相信什么都可以。也可以这么说:世界的本质就是我的信念。我相信世界的本质是什么,它就是什么。</p><p>那么,“人生的意义是什么”该怎么回答呢?这也是一个信仰问题。没有固定答案的世界才更美好.</p><p>那么,该如何找到自己的人生意义?我认为最有效的办法,是逼迫自己直面死亡。我们问人生的意义是什么,其实就是在给自己的人生找一个目标,就是在问:我为什么活着?这也就等于在问:我为什么不立刻自杀?假如你能顺利地回答“我为什么不自杀”的问题,如“我不想死,是因为我还想到处旅游,吃好吃的,我不想死是因为我不能让父母伤心”。那么,这些答案就是你现在的人生意义。假如你的回答是“我不觉得活着有什么意义,我只是怕死”,那就请你想象一下死亡来临时的感觉吧。一个无神论者在面临死亡的巨大恐惧的时候,有时,求生的本能会让头脑拼命地给自己寻找活下去的理由。这个理由,也就是每个人的人生意义。有些和死神擦肩而过的人说,经过这一场磨难,自己大彻大悟,对人生有了更高层次的看法了。可是,我们每个人都知道自己早晚会死,为什么非要死到临头的时候才会大彻大悟呢?那就是因为绝大部分人平时从不愿意直面死亡,潜意识里认为自己可以永远逃避死亡。所以只有死到临头,才会开始反省人生。我们既然知道了这个道理,那就不妨早一点直面死亡,早一点把这件事想明白。以人类现有的经验而言,死亡是宇宙中少有的一个不可逆的事情。人死以后,再也回不到原来的生活中,想后悔也来不及了。如果死亡确实是意识的永远终结,那么就意味着我们失去了一切探索世界的机会。我们甚至可以感性地说,那就意味着我们自愿放弃了这世界给我们最大的恩赐,而这恩赐就只有这一次,放弃了就没有了。</p>]]></content>
<summary type="html">
<h1 id="西方哲学史"><a href="#西方哲学史" class="headerlink" title="西方哲学史"></a>西方哲学史</h1><blockquote>
<p>摘录自 林欣浩 &lt;&lt;哲学家们都干了些什么?&gt;&gt;</p>
</blo
</summary>
<category term="读书" scheme="http://yoursite.com/categories/%E8%AF%BB%E4%B9%A6/"/>
<category term="哲学史" scheme="http://yoursite.com/tags/%E5%93%B2%E5%AD%A6%E5%8F%B2/"/>
</entry>
<entry>
<title>读<<穷爸爸富爸爸>></title>
<link href="http://yoursite.com/2021/10/19/%E8%AF%BB%3C%3C%E7%A9%B7%E7%88%B8%E7%88%B8%E5%AF%8C%E7%88%B8%E7%88%B8%3E%3E/"/>
<id>http://yoursite.com/2021/10/19/读<<穷爸爸富爸爸>>/</id>
<published>2021-10-19T03:47:30.000Z</published>
<updated>2021-08-31T03:51:19.126Z</updated>
<content type="html"><![CDATA[<h1 id="读-lt-lt-穷爸爸富爸爸-gt-gt"><a href="#读-lt-lt-穷爸爸富爸爸-gt-gt" class="headerlink" title="读<<穷爸爸富爸爸>>"></a>读<<穷爸爸富爸爸>></h1><h3 id="第一课-富人不为钱工作"><a href="#第一课-富人不为钱工作" class="headerlink" title="第一课 富人不为钱工作"></a>第一课 富人不为钱工作</h3><p>生活推着我们所有的人,有些人放弃了,有些人在抗争。少数人学会了这门课程,取得了进步,他们欢迎生活来推动他们,对他们来说,这种推动意味着他们需要并愿意去学习一些东西。他们学习,然后取得进步。但大多数人放弃了,还有一部分人像你一样在抗争。</p><p>有些人因为他们和他们的家庭需要钱而接受这份工资。但他们所做的也只是等待,等待加薪,因为他们认为更多的钱能解决问题。大部分人接受这样的工资,还有一些人会再找一份工作,仍旧干得很努力,但仍只能得到很少的报酬。</p><p>正是出于恐惧的心理,人们才想找一份安稳的工作。这些恐惧有:害怕付不起账单,害怕被解雇,害怕没有足够的钱,害怕重新开始。为了寻求保障,他们会学习某种专业,或是做生意,拼命为钱而工作。大多数人成了钱的奴隶,然后就把怒气发泄在他们老板身上。</p><p>大多数人都希望有一份工资收入,因为他们都有恐惧和贪婪之心。一开始,没钱的恐惧会促使他们努力工作,得到报酬后,贪婪或欲望又让他们想拥有所有用钱能买到的好东西。于是就形成了一种模式。起床,上班,付账,再起床,再上班,再付账他们的生活从此被这两种感觉所控制:恐惧和贪婪。给他们更多的钱,他们就会以更高的开支重复这种循环。</p><p>当你长大后,你想要的玩具会更贵,会变成要让你的朋友羡慕的汽车、游艇和大房子,恐惧把你推出门外,欲望又开始召唤你,诱惑你去触礁。这就是陷阱。钱就是驴子面前的胡萝卜,是幻象。如果驴子能了解到全部事实,它可能会重新想想是否还要去追求胡萝卜。</p><p>你们越快忘记你们的工资,你们未来的生活就会越轻松,继续用你们的头脑思考,不求回报地工作,很快就会发现比拿工资更挣钱的方法。你们会看到别人看不见的东西。机会就摆在人们面前,但大多数人从来看不到这些机会,因为他们忙着追求金钱和安定,所以只能得到这些。如果你们能看到一个机会,就注定你们会在一生中不断地发现机会。</p><h3 id="第二课:为什么要学习财务知识"><a href="#第二课:为什么要学习财务知识" class="headerlink" title="第二课:为什么要学习财务知识"></a>第二课:为什么要学习财务知识</h3><p>我想有太多人过多地关注钱,而不是关注他们最大的财富——所受的教育。如果人们能灵活一些,保持开放的头脑不断学习,他们将在时代的变化中一天天地富有起来。如果人们认为钱能解决一切问题,恐怕他们的日子就不会太好过。只有知识才能解决问题并创造财富,那些不是靠财务知识挣来的钱也不会长久。从长远来看,重要的不是你挣了多少钱,而是你能留下多少钱,以及能够留住多久。</p><p>你必须明白资产和负债的区别,并且购买资产。这就是第一条规则,也是唯一一条规则。</p><p>资产是能把钱放进你口袋里的东西。负债是把钱从你口袋里取走的东西。<br>富爸爸和穷爸爸在对待房子问题上的不同观念,一个认为他的房子是资产,另一个则认为是负债。拥有房子后带来的附属支出,房子越大支出就越大,现金就会通过支出不断地流出。我很清楚那不是资产,而是负债,因为它把钱从我们口袋中掏走了。</p><ol><li>对于房子,我要指出大多数人一生都在为一所他们从未真正拥有的房子而辛苦地工作。</li><li>即使人们住房按揭贷款的利息是免税的,他们还是要先还清各期贷款后,才能以税后收入支付各种开支。</li><li>财产税。</li><li>房子的价值并不总是在上升。</li><li>最大的损失是致富机会的损失。如果你所有的钱都投在了房子上,你就不得不努力工作,因为你的现金正不断地从支出项流出,而不是流入资产项,这是典型的中产阶级现金流模式。</li><li>失去了用其他资产增值的时机。</li><li>本可以用来投资的资本将用于支付房子高额的维修和保养费用。</li><li>失去受教育的机会。</li></ol><p>为什么富人会越来越富。资产项产生的收入足以弥补支出,还可以用剩余的收入对资产项进行再投资。资产项不断增长,相应的收入也会越来越多。弄清资产与负债的区别,一旦你明白了这种区别,你就会竭尽全力只买入能带来收入的资产,这是你走上致富之路的最好办法。坚持下去,你的资产就会不断增加。同时还要注意降低负债和支出,这也会让你有更多的钱投入资产项。很快,你就会有钱来考虑进行一些投资了,这些投资能给你带来100%,甚至是无限的回报。</p><h3 id="第三课:关注自己的事业"><a href="#第三课:关注自己的事业" class="headerlink" title="第三课:关注自己的事业"></a>第三课:关注自己的事业</h3><p>存在财务问题的人经常耗费一生为别人工作,其中许多人在他们不能工作时就变得一无所有。请注意,你的职业和你的事业有很大的差别。我经常问人们:你的事业是什么?他们会说:我在银行工作。接着我问他们是否拥有一家银行,他们通常回答:不是的,我只在那儿工作。</p><p>对成年人而言,把支出保持在低水平、减少借款并勤劳地工作会帮你打下一个稳固的资产基础。</p><p>富人与穷人一个重要的区别就是:富人最后才买奢侈品,而穷人和中产阶级会先买下诸如大房子、珠宝、皮衣、宝石、游艇等奢侈品,因为他们想让自己看上去很富有。真正的奢侈品是对投资和积累真正资产的奖励。例如,当我通过房地产生意获得了额外的收入时,去买了辆奔驰汽车。这不是因为增加工作量或是冒着风险才买下的,是在房地产生意上的收益支付了这辆车。</p><h3 id="第四课:税收的历史和公司的力量"><a href="#第四课:税收的历史和公司的力量" class="headerlink" title="第四课:税收的历史和公司的力量"></a>第四课:税收的历史和公司的力量</h3><p>事实上富人并未被征税,是中产阶级尤其是受过良好教育的高收入的中产阶级在为穷人支付税金。税收的初衷是惩罚有钱人,而现实却是它惩罚了对它投赞同票的中产阶级和穷人。政府对钱的胃口越来越大,以致中产阶级也要被征税,且税收的范围不断向穷人扩展。</p><p>财商是由4个方面的专门知识构成的:<br>第一是会计,也就是我说的财务知识。<br>第二是投资,我把它称为钱生钱的科学。<br>第三是了解市场,它是供给与需求的科学。这要求了解受感情驱动的市场的技术面。<br>第四是法律。例如:了解减税优惠政策和公司法的人会比雇员和小业主更快致富。</p><h3 id="第五课:富人的投资"><a href="#第五课:富人的投资" class="headerlink" title="第五课:富人的投资"></a>第五课:富人的投资</h3><p>我意识到过分的害怕和自我怀疑是毁掉我们才能的最大因素。看到有些人明明知道该做什么,却缺乏勇气付诸实际,我就感到十分悲哀。在现实生活中,人们往往是依靠勇气而不是智慧去取得领先的位置的。</p><p>300年前,土地是一种财富,所以,谁拥有土地,谁就拥有财富。后来,美国依靠工厂和工业产品上升为世界头号强国,工业家占有了财富。今天,信息便是财富。问题是,信息以光一样的速度在全世界迅速传播,这种新的财富不再像土地和工厂那样具有明确的范围和界限。变化会越来越快,越来越显著,百万富翁的人数会极大地增加,同样,也会有许多人被远远地抛在后面。</p><p>如果你清楚自己在做什么,就不是在赌博;如果你把钱投进一笔交易然后只是祈祷,才是在赌博。在任何一项投资中,成功的办法都是运用你的技术知识、智慧以及对于这个游戏的热爱来减少意外、降低风险。当然,风险总是存在的,但你的财商可以提高你应付意外的能力。常常有这样的情况,对一个人来说是高风险的事情,对另一个人来说则可能是低风险的。这就是我不断鼓励人们多关注财商教育而不只是投资股票、房地产或其他市场的原因。你越精明,就越能应付意外情况。</p><h3 id="第六课:学会不为钱工作"><a href="#第六课:学会不为钱工作" class="headerlink" title="第六课:学会不为钱工作"></a>第六课:学会不为钱工作</h3><p>一位药品贸易的商务顾问曾经告诉我,有许多医生、牙医和按摩师在财务上困难重重。以前我总是认为他们一毕业,美元就会滚滚而来。这位商务顾问还跟我说了一句话:他们只有一项技能,所以挣不到大钱。这句话的意思是说,大部分人需要学习和掌握不止一项技能,只有这样他们的收入才能获得显著增长。以前我提到过,财商是会计、投资、市场和法律等各方面知识和能力的综合。将上述4种技能结合起来,以钱生钱就会容易得多。当涉及钱的时候,只有一项技能的人不得不努力工作。</p><p>一种可怕的管理理论是这样说的:工人付出最大努力以免于被解雇,而雇主提供最低工资以防止工人辞职。如果你看一看大部分公司的工资支付额度,就会明白这一说法确实在某种程度上道出了真相。最终的结果是大部分人从不敢越雷池一步,他们按照别人教他们的那样去做:找一份稳定的工作。</p><h3 id="克服困难"><a href="#克服困难" class="headerlink" title="克服困难"></a>克服困难</h3><p>掌握财务知识的人有时候还是不能积累丰厚的资产项,其主要原因有5个:</p><ol><li>对可能亏钱的 恐惧心理。</li></ol><p>如果你讨厌冒险,担心会亏钱,就早点动手积累资产吧。如果你有致富的愿望,就必须集中精力。把你大部分的鸡蛋放在较少的篮子里,别像穷人和中产阶级那样:把很少的鸡蛋放在许多篮子里。</p><ol><li>愤世嫉俗。</li></ol><p>我认为疑虑和愤世嫉俗的心态使大多数人安于贫困。生活等着你去致富,可就是这些疑虑使人们无法摆脱贫穷。未经证实的怀疑和恐惧会使人们成为愤世嫉俗者。愤世者抱怨现实,而成功者分析现实。<br>如果大多数人懂得股票市场上横盘(预定低点抛售)是什么意思的话,就会有更多的人为了赢利而投资,而不是为了避免损失而投资。横盘好比一个计算机指令,当股价开始下跌时自动卖出股票,帮助你将损失最小化、收益最大化。对于那些害怕受到损失的人来说,这是一项极好的工具。</p><ol><li>懒惰。</li></ol><p>我可付不起带来的悲哀和无助感会使人们失望、迟钝以至意志消沉。我怎样才能付得起则打开了充满可能性的快乐和梦想之门。<br>世界之所以发展是因为我们都渴望生活得更好,新发明的诞生是因为我们渴望更好的东西,我们努力学习也是因为我们想要了解更好的东西。因此,每当你发现自己在逃避你内心清楚应该去做的事情时,就应该问问自己:我还能得到什么?稍稍贪婪一点,这是治愈懒惰的灵丹妙药。</p><ol><li><p>不良习惯。</p></li><li><p>自负。</p></li></ol><h3 id="开始行动"><a href="#开始行动" class="headerlink" title="开始行动"></a>开始行动</h3><p>我建议你采取以下步骤来开发上帝赐予你的才能,这种才能只有你才可以控制:</p><ol><li><p>你需要一个超现实的理由</p></li><li><p>每天作出自己的选择</p></li></ol><p>从理财的角度来说,我们每挣到一美元,就得到了一次选择自己是成为富人、穷人还是中产阶级的机会。我们花钱的习惯反映了我们是什么样的人,穷人之所以贫穷是因为他们有着不良的消费习惯。</p><ol><li>慎重地选择朋友</li></ol><p>首先,我不会把理财状况作为挑选朋友的标准。我既有穷困潦倒的朋友,也有每年都有数百万美元进账的朋友,因为我相信三人行,必有我师,我愿意努力向他们学习。<br>不要听贫穷的或是胆小的人的话。他们总会告诉你一件事为什么不可行。<br>在积累财富的过程中,最困难的事情莫过于坚持自己的选择而不盲目从众。因为在竞争激烈的市场上,群体往往会反应迟钝,成为被宰割的对象。</p><ol><li>掌握一种模式,然后再学习一种新的模式——快速学习的力量。</li></ol><p>如果你对自己所做的工作感到厌倦或是你挣的钱不够多,那么很简单,改变你的挣钱模式吧。在今天这个快速变化的社会中,你学到的东西再多都不算多,因为当你学到时往往就已经过时了。问题在于你学得有多快,这种技能是无价之宝。如果你想赚钱,寻找一条捷径是非常关键的。</p><ol><li>首先支付自己——自律的力量。</li></ol><p>如果你控制不了自己,就别想着致富。能否自律是将富人、穷人和中产阶级区分开来的首要因素。<br>不要背上数额过大的债务包袱。要保持低支出。首先增加自己的资产,然后,再用资产项产生的现金流来消费.储蓄只能用于创造更多的收入,而不是用来支付账单。</p>]]></content>
<summary type="html">
<h1 id="读-lt-lt-穷爸爸富爸爸-gt-gt"><a href="#读-lt-lt-穷爸爸富爸爸-gt-gt" class="headerlink" title="读&lt;&lt;穷爸爸富爸爸&gt;&gt;"></a>读&lt;&lt;穷爸爸富爸爸&gt;&gt;
</summary>
<category term="读书" scheme="http://yoursite.com/categories/%E8%AF%BB%E4%B9%A6/"/>
<category term="读书" scheme="http://yoursite.com/tags/%E8%AF%BB%E4%B9%A6/"/>
</entry>
<entry>
<title>Webpack 配置指南</title>
<link href="http://yoursite.com/2021/09/08/Webpack%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/"/>
<id>http://yoursite.com/2021/09/08/Webpack配置指南/</id>
<published>2021-09-08T02:52:36.000Z</published>
<updated>2022-03-23T09:58:04.073Z</updated>
<content type="html"><![CDATA[<h1 id="webpack-配置指南"><a href="#webpack-配置指南" class="headerlink" title="webpack 配置指南"></a>webpack 配置指南</h1><ol><li>搭建环境</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">npm init -y <span class="comment"># 生成 package.json</span></span><br><span class="line">npm i webpack --save-dev <span class="comment"># 引入 webpack</span></span><br><span class="line">npm i webpack-cli --save-dev <span class="comment"># 引入 webpack-cli</span></span><br></pre></td></tr></table></figure><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">"scripts": {</span><br><span class="line"> "build": "webpack",</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>webpack 旧版本必须在 webpack.config.js 通过 entry 属性定义入口,现在默认为<code>./src/index.js</code></p><p>新建<code>./src/index.js</code>文件.执行命令后,<code>dist/main.js</code>为默认输出.</p><ol><li>生产和开发模式<br>webpack 分为 ‘development’ 或 ‘production’ 或 ‘none’.<br>production 可以开箱即用地进行各种优化。 包括压缩,作用域提升,tree-shaking 等<br>development 针对速度进行了优化,仅仅提供了一种不压缩的 bundle.</li></ol><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">"scripts": {</span><br><span class="line"> "build": "webpack --mode production",</span><br><span class="line"> "dev": "webpack --mode development"</span><br><span class="line">},</span><br></pre></td></tr></table></figure><ol><li>默认覆盖 entry 和 output<br>webpack 支持 ES6, CommonJS, AMD 规范</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * webpack 支持 ES6、CommonJs 和 AMD 规范</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">import</span> sum <span class="keyword">from</span> <span class="string">'./vendor/sum'</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'sum(1, 2) = '</span>, sum(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment">// CommonJs</span></span><br><span class="line"><span class="keyword">var</span> minus = <span class="built_in">require</span>(<span class="string">'./vendor/minus'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'minus(1, 2) = '</span>, minus(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment">// AMD</span></span><br><span class="line"><span class="built_in">require</span>([<span class="string">'./vendor/multi'</span>], <span class="function"><span class="keyword">function</span> (<span class="params">multi</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'multi(1, 2) = '</span>, multi(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>webpack.config.js 是 webpack 默认的配置文件名,在根目录下创建.we</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> entry: {</span><br><span class="line"> app: <span class="string">'./app.js'</span>, <span class="comment">// 需要打包的文件入口</span></span><br><span class="line"> },</span><br><span class="line"> output: {</span><br><span class="line"> publicPath: __dirname + <span class="string">'/dist/'</span>, <span class="comment">// js 引用的路径或者 CDN 地址</span></span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>), <span class="comment">// 打包文件的输出目录</span></span><br><span class="line"> filename: <span class="string">'bundle.js'</span>, <span class="comment">// 打包后生产的 js 文件</span></span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。<br><code>__dirname</code>: 当前模块的文件夹路径。</p><p>执行 build 后会生成多个 bundle 文件,这跟 AMD 的引入方式有关,在实际写代码的时候,最好使用 ES6 和 CommonJS 的规范来写.</p><p>// CleanWebpackPlugin 插件可以自动删除 dist 旧文件在打包.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install clean-webpack-plugin --save-dev</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> { CleanWebpackPlugin } = <span class="built_in">require</span>(<span class="string">'clean-webpack-plugin'</span>)</span><br><span class="line">plugins: [</span><br><span class="line"> <span class="keyword">new</span> CleanWebpackPlugin(), <span class="comment">// 默认情况下,此插件将删除 webpack output.path目录中的所有文件,以及每次成功重建后所有未使用的 webpack 资产。</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><ol><li>babel 转译</li></ol><p>webpack 使用 loader 进行转译.babel-loader 可以将 es6 转译为 es5.<br>我们使用 babel7 版本.<br>@babel/core<br>@babel/preset-env: 包含 ES6、7 等版本的语法转化规则<br>@babel/plugin-transform-runtime: 可以重复使用 Babel 注入的程序代码来节省代码,减小体积.避免 polyfill 污染全局变量,减小打包体积<br>@babel/polyfill: ES6 内置方法和函数转化垫片,所谓垫片也就是垫平不同浏览器或者不同环境下的差异<br>babel-loader: 负责 ES6 语法转化</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime --save-dev</span><br><span class="line">npm i @babel/polyfill @babel/runtime</span><br></pre></td></tr></table></figure><p>在项目根目录创建<code>.babelrc</code>配置 babel</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"presets"</span>: [<span class="string">"@babel/preset-env"</span>],</span><br><span class="line"> <span class="attr">"plugins"</span>: [<span class="string">"@babel/plugin-transform-runtime"</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>webpack 配置 loader.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.js$/</span>, <span class="comment">// 使用正则来匹配 js 文件</span></span><br><span class="line"> exclude: <span class="regexp">/node_modules/</span>, <span class="comment">// 排除依赖包文件夹</span></span><br><span class="line"> use: {</span><br><span class="line"> loader: <span class="string">'babel-loader'</span>, <span class="comment">// 使用 babel-loader</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在入口文件处全局引入<code>import @babel/polyfill</code>,执行 build 命令打包.<br>全局引入 @babel/polyfill 的这种方式可能会导入代码中不需要的 polyfill,从而使打包体积更大.<br>更改 .babelrc,只转译我们使用到的.</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"presets"</span>: [</span><br><span class="line"> [</span><br><span class="line"> <span class="string">"@babel/preset-env"</span>,</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"useBuiltIns"</span>: <span class="string">"usage"</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"plugins"</span>: [<span class="string">"@babel/plugin-transform-runtime"</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注释入口文件全局引入的<code>import @babel/polyfill</code>,执行 build 命令打包.打包体积明显变小.</p><p><code>.browserslistrc</code>用于在不同前端工具之间共享目标浏览器和 Node.js 版本的配置,或在 package 文件中加入</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">"browserslist": [</span><br><span class="line"> "> 1%",</span><br><span class="line"> "last 2 version",</span><br><span class="line"> <span class="string">"not ie <= 8"</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><ol><li>代码切割 code splitting</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i lodash</span><br></pre></td></tr></table></figure><p>新建<code>src/index.js</code>并添加 lodash 相关代码,更改 webpack 配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">entry: {</span><br><span class="line"> main: <span class="string">'./src/index.js'</span></span><br><span class="line">},</span><br><span class="line">output: {</span><br><span class="line"> publicPath: __dirname + <span class="string">'/dist/'</span>, <span class="comment">// js 引用的路径或者 CDN 地址</span></span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>), <span class="comment">// 打包文件的输出目录</span></span><br><span class="line"> filename: <span class="string">'[name].bundle.js'</span>, <span class="comment">// 代码打包后的文件名</span></span><br><span class="line"> chunkFilename: <span class="string">'[name].js'</span> <span class="comment">// 代码拆分后的文件名</span></span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>运行 build 打包我们会发现,业务代码和第三方框架一起被打包,业务代码更新频繁,而第三方框架不会变,这会造成资源浪费和低效率.<br>浏览器是有缓存的,如果文件没变动的话,就不用再去发送 http 请求,而是直接从缓存中取,这样在刷新页面或者第二次进入的时候可以加快网页加载的速度。<br>所以我们可以利用 webpack 的代码分割功能,将第三方代码与业务代码分开,这样及时业务变动,浏览器也可以读取第三方框架的缓存.</p><p>webpack4 使用 内置的 splitChunksPlugins 进行代码分割. all 表示分割所有代码, async 为默认值,表示分割异步代码.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">optimization: {</span><br><span class="line"> splitChunks: {</span><br><span class="line"> chunks: <span class="string">'all'</span></span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>执行 build 命令,发现文件为<code>dist/**.bundle.js dist/vendors-***.js</code>,表示代码分割成功.<br>对分割名称进行更改.添加 cacheGroups 对象.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">optimization: {</span><br><span class="line"> splitChunks: {</span><br><span class="line"> chunks: <span class="string">'all'</span>,</span><br><span class="line"> cacheGroups: {</span><br><span class="line"> vendors: {</span><br><span class="line"> name: <span class="string">'vendors'</span>,</span><br><span class="line"> chunks: <span class="string">'all'</span>,</span><br><span class="line"> <span class="comment">// test 正则过滤</span></span><br><span class="line"> <span class="comment">// priority 优先级</span></span><br><span class="line"> <span class="comment">// minChunks 最小公用多少次才打包</span></span><br><span class="line"> <span class="comment">// minSize 超过多少进行压缩</span></span><br><span class="line"> <span class="comment">// reuseExistingChunk: true // 公共模块必开启,如果当前块已从主模块拆分出来,则将重用它而不是生成新的块</span></span><br><span class="line"></span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">default</span>: {</span><br><span class="line"> chunks: <span class="string">'all'</span>,</span><br><span class="line"> minChunks: <span class="number">2</span>,</span><br><span class="line"> priority: <span class="number">-20</span>,</span><br><span class="line"> reuseExistingChunk: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>懒加载,Prefetching 预加载</li></ol><p>懒加载就是通过 import 去异步的加载一个模块,类似于 vue-router 中的懒加载<code>component: () => import('***.vue')</code>.<br>懒加载并不是 webpack 里的概念,而是 ES6 中的 import 语法,webpack 只是能够识别 import 语法,能进行代码分割而已。<br>页面加载时,异步的代码不会执行但是确下载下来了,浪费了页面性能,我们希望 webpack 可以把异步代码放到一个模块中.当我们需要时,才会去加载这些代码.这也是为什么 webpack 的 splitChunks 中的 chunks 默认是 async,异步的.</p><p>问题又来了,如果异步的代码模块较大,而当我们需要加载时就要等待,体验不好.webpack 的 Prefetching/Preloading 预加载功能可以解决这个问题.它会在网络带宽空闲的时候预先帮我们加载.</p><p>webpackPrefetch: 等待核心代码加载完之后,有空闲之后再去加载<br>webpackPreload: 和核心的代码并行加载,不推荐</p><p>使用方式<code>import(/* webpackPrefetch: true */, '......js').then...</code></p><ol><li>自动生成 html 文件并自动引入相关资源</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i html-webpack-plugin html-loader --save-dev</span><br></pre></td></tr></table></figure><p>更改 webpack 配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line">plugins: [</span><br><span class="line"> <span class="keyword">new</span> HtmlWebpackPlugin({</span><br><span class="line"> <span class="comment">// 打包输出HTML</span></span><br><span class="line"> title: <span class="string">'自动生成 HTML'</span>,</span><br><span class="line"> minify: {</span><br><span class="line"> <span class="comment">// 压缩 HTML 文件</span></span><br><span class="line"> removeComments: <span class="literal">true</span>, <span class="comment">// 移除 HTML 中的注释</span></span><br><span class="line"> collapseWhitespace: <span class="literal">true</span>, <span class="comment">// 删除空白符与换行符</span></span><br><span class="line"> minifyCSS: <span class="literal">true</span>, <span class="comment">// 压缩内联 css</span></span><br><span class="line"> },</span><br><span class="line"> filename: <span class="string">'index.html'</span>, <span class="comment">// 生成后的文件名</span></span><br><span class="line"> template: <span class="string">'index.html'</span>, <span class="comment">// 根据此模版生成 HTML 文件</span></span><br><span class="line"> }),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>由于使用了 title, 在 index.html 中加<code><title><%= htmlWebpackPlugin.options.title %></title></code>,执行命令,dist 中生成 index 的 html 文件.我们发现其中 js 引入为绝对路径,需要更改为相对路径.找到 output 输出配置,更改 publicPath 公共路径,修改为 <code>./</code></p><ol><li>处理 css 文件</li></ol><p>这次我们需要用到 css-loader,style-loader 等 loader.<br>css-loader:负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 <code>@import 和 url()</code>等引用外部文件的声明.<br>style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i css-loader style-loader --save-dev</span><br></pre></td></tr></table></figure><p>更改配置文件</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.css$/</span>, <span class="comment">// 针对 .css 后缀的文件设置 loader</span></span><br><span class="line"> use: [<span class="string">'style-loader'</span>, <span class="string">'css-loader'</span>],</span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>可以发现在 style 标签中出现了相关 css,如果要单独打包成文件,需要 mini-css-extract-plugin 插件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i mini-css-extract-plugin --save-dev</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> MiniCssExtractPlugin = <span class="built_in">require</span>(<span class="string">'mini-css-extract-plugin'</span>)</span><br><span class="line"><span class="keyword">new</span> MiniCssExtractPlugin({</span><br><span class="line"> filename: <span class="string">'[name].css'</span>,</span><br><span class="line"> chunkFilename: <span class="string">'[id].css'</span>,</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.css$/</span>,</span><br><span class="line"> use: [</span><br><span class="line"> {</span><br><span class="line"> loader: MiniCssExtractPlugin.loader,</span><br><span class="line"> },</span><br><span class="line"> <span class="string">'css-loader'</span>,</span><br><span class="line"> ],</span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>执行 build,生成了单独的 css 文件,但是并没有压缩,引入 optimize-css-assets-webpack-plugin 插件来实现 css 压缩.<br>webpack5 使用 css-minimizer-webpack-plugin 插件.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install css-minimizer-webpack-plugin --save-dev</span><br></pre></td></tr></table></figure><p>更改配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> CssMinimizerPlugin = <span class="built_in">require</span>(<span class="string">'css-minimizer-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line">optimization: {</span><br><span class="line"> minimizer: [<span class="keyword">new</span> CssMinimizerPlugin()]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>处理 scss 或 less.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i node-sass sass-loader --save-dev</span><br></pre></td></tr></table></figure><p>更改 loader:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> test: <span class="regexp">/\.(scss|css)$/</span>,</span><br><span class="line"> use: [</span><br><span class="line"> {</span><br><span class="line"> loader: MiniCssExtractPlugin.loader</span><br><span class="line"> },</span><br><span class="line"> <span class="string">'css-loader'</span>,</span><br><span class="line"> <span class="string">'sass-loader'</span></span><br><span class="line"> ]</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>根据 webpack 规则:放在最后的 loader 首先被执行,从上往下写的话是下面先执行,从左往右写的话是右边先执行。</p><p>为 css 加上浏览器前缀.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install postcss-loader autoprefixer --save-dev</span><br></pre></td></tr></table></figure><p>新建 postcss.config.js ,并修改 loader 配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> plugins: [<span class="built_in">require</span>(<span class="string">'autoprefixer'</span>)]</span><br><span class="line">}</span><br><span class="line">{</span><br><span class="line"> test: <span class="regexp">/\.(scss|css)$/</span>,</span><br><span class="line"> use: [</span><br><span class="line"> {</span><br><span class="line"> loader: MiniCssExtractPlugin.loader</span><br><span class="line"> },</span><br><span class="line"> <span class="string">'css-loader'</span>,</span><br><span class="line"> <span class="string">'postcss-loader'</span></span><br><span class="line"> <span class="string">'sass-loader'</span></span><br><span class="line"> ]</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><ol><li>Tree Shaking<br>字面意思是摇树,项目中没有使用的代码会在打包的时候丢掉。JS 的 Tree Shaking 依赖的是 ES6 的模块系统 import 和 export.<br>对于经常使用的第三方库,要安装 ES 写法的版本,比如 <code>lodash-es</code></li></ol><p>webpack5 可以通过 package.json 的 “sideEffects”(意为副作用) 属性,通知 webpack 安全的删除未使用的 export.而 <code>optimization.usedExports: true,</code>依赖于 terser 去检测语句中的副作用.</p><p>css tree shaking: 略.</p><ol><li>图片字体资源的处理</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install url-loader file-loader --save-dev</span><br></pre></td></tr></table></figure><p>更改配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.(png|jpg|jpeg|gif)$/</span>,</span><br><span class="line"> use: [</span><br><span class="line"> {</span><br><span class="line"> loader: <span class="string">'url-loader'</span>,</span><br><span class="line"> options: {</span><br><span class="line"> name: <span class="string">'[name]-[hash:5].min.[ext]'</span>,</span><br><span class="line"> outputPath: <span class="string">'images/'</span>, <span class="comment">//输出到 images 文件夹</span></span><br><span class="line"> limit: <span class="number">20000</span>, <span class="comment">//把小于 20kb 的文件转成 Base64 的格式</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> ],</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.(eot|woff2?|ttf|svg)$/</span>,</span><br><span class="line"> use: [</span><br><span class="line"> {</span><br><span class="line"> loader: <span class="string">'url-loader'</span>,</span><br><span class="line"> options: {</span><br><span class="line"> name: <span class="string">'[name]-[hash:5].min.[ext]'</span>,</span><br><span class="line"> limit: <span class="number">5000</span>, <span class="comment">// fonts file size <= 5KB, use 'base64'; else, output svg file</span></span><br><span class="line"> publicPath: <span class="string">'fonts/'</span>,</span><br><span class="line"> outputPath: <span class="string">'fonts/'</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> ],</span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure><ol><li>处理第三方 js 库</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">resolve: {</span><br><span class="line"> alias: {</span><br><span class="line"> jQuery$: path.resolve(__dirname, <span class="string">'src/vendor/jquery.min.js'</span>)</span><br><span class="line"> }</span><br><span class="line">},</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> webpack.ProvidePlugin({</span><br><span class="line"> $: <span class="string">'jquery'</span>, <span class="comment">// npm</span></span><br><span class="line"> jQuery: <span class="string">'jQuery'</span> <span class="comment">// 本地Js文件</span></span><br><span class="line"> })</span><br></pre></td></tr></table></figure><ol><li>webpack-dev-server</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i webpack-dev-server --save-dev</span><br></pre></td></tr></table></figure><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">"scripts": {</span><br><span class="line"> "dev": "webpack-dev-server --open",</span><br><span class="line"> "build": "webpack --mode production"</span><br><span class="line"> },</span><br></pre></td></tr></table></figure><p>配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mode: <span class="string">'development'</span>, <span class="comment">// 开发模式</span></span><br><span class="line">devtool: <span class="string">'source-map'</span>, <span class="comment">// 开启调试</span></span><br><span class="line">devServer: {</span><br><span class="line">port: <span class="number">8080</span>,</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>DllPlugin 加快打包速度</li></ol><p>新建 webpack.dll.js 文件,</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> mode: <span class="string">'production'</span>,</span><br><span class="line"> entry: {</span><br><span class="line"> vendors: [<span class="string">'lodash'</span>, <span class="string">'jquery'</span>],</span><br><span class="line"> },</span><br><span class="line"> output: {</span><br><span class="line"> filename: <span class="string">'[name].dll.js'</span>,</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dll'</span>),</span><br><span class="line"> library: <span class="string">'dll_[name]'</span>,</span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> webpack.DllPlugin({</span><br><span class="line"> name: <span class="string">'dll_[name]'</span>,</span><br><span class="line"> path: path.join(__dirname, <span class="string">'dll'</span>, <span class="string">'[name].manifest.json'</span>),</span><br><span class="line"> context: __dirname,</span><br><span class="line"> }),</span><br><span class="line"> ],</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>json 文件中新增命令</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">"build:dll": "webpack --config ./build/webpack.dll.js"</span><br></pre></td></tr></table></figure><p>在 webpack 配置中添加</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> AddAssetHtmlPlugin = <span class="built_in">require</span>(<span class="string">'add-asset-html-webpack-plugin'</span>) <span class="comment">// dll自动引入</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// dll配置</span></span><br><span class="line"><span class="keyword">const</span> dllReference = <span class="function">(<span class="params">config</span>) =></span> {</span><br><span class="line"> config.plugin(<span class="string">'vendorDll'</span>).use(webpack.DllReferencePlugin, [</span><br><span class="line"> {</span><br><span class="line"> context: __dirname,</span><br><span class="line"> manifest: <span class="built_in">require</span>(<span class="string">'./dll/vendor.manifest.json'</span>),</span><br><span class="line"> },</span><br><span class="line"> ])</span><br><span class="line"></span><br><span class="line"> config</span><br><span class="line"> .plugin(<span class="string">'addAssetHtml'</span>)</span><br><span class="line"> .use(AddAssetHtmlPlugin, [</span><br><span class="line"> [</span><br><span class="line"> {</span><br><span class="line"> filepath: <span class="built_in">require</span>.resolve(</span><br><span class="line"> path.resolve(__dirname, <span class="string">'dll/vendor.dll.js'</span>)</span><br><span class="line"> ),</span><br><span class="line"> outputPath: <span class="string">'dll'</span>,</span><br><span class="line"> publicPath: <span class="string">'/dll'</span>,</span><br><span class="line"> },</span><br><span class="line"> ],</span><br><span class="line"> ])</span><br><span class="line"> .after(<span class="string">'html'</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">chainWebpack: <span class="function">(<span class="params">config</span>) =></span> {</span><br><span class="line"> dllReference(config)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li><p>多页面打包<br>在 webpack.base.conf.js 中配置 entry,配置两个入口.然后多次调用 new HtmlWebpackPlugin({})插件.或写个函数自动生成配置.</p></li><li><p>其他</p></li></ol><ul><li>webpack-merge 用来合并 webpack 配置</li><li>workbox-webpack-plugin 用来配置 PWA</li><li>ts-loader 以及 ts.config.js 用来配置 ts</li><li>eslint-loader 以及 .eslintrc.js</li></ul><ol><li>编写 loader</li></ol><p>新建一个 js 文件</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">source</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> source.replace(<span class="string">'world'</span>, <span class="keyword">this</span>.query.name)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>source 参数就是我们的源代码,这里是将源码中的 world 替换成我们自定义的 字符串.</p><p>修改 webpack 配置添加自定义 loader.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/.js/</span>,</span><br><span class="line"> use: [</span><br><span class="line"> {</span><br><span class="line"> loader: path.resolve(__dirname, <span class="string">'./loaders/replaceLoader.js'</span>),</span><br><span class="line"> options: {</span><br><span class="line"> name: <span class="string">'xh'</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> ], <span class="comment">// 引入自定义 loader</span></span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure><ol><li>编写 plugin</li></ol><p>新建 xxx-webpack-plugin 的 js 文件,plugin 是一个类, 调用的时候要 new 这个类生成实例.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CopyrightWebpackPlugin</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(options) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'插件被使用了'</span>, <span class="string">'参数为'</span>, options)</span><br><span class="line"> }</span><br><span class="line"> apply(compiler) {</span><br><span class="line"> <span class="comment">// compiler 是 webpack 的实例</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = CopyrightWebpackPlugin</span><br></pre></td></tr></table></figure><p>调用:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> CopyrightWebpackPlugin = <span class="built_in">require</span>(<span class="string">'./plugins/copyright-webpack-plugin'</span>)</span><br><span class="line">plugins: [</span><br><span class="line"><span class="keyword">new</span> CopyrightWebpackPlugin({</span><br><span class="line">name: <span class="string">'xx'</span></span><br><span class="line">})</span><br><span class="line">],</span><br></pre></td></tr></table></figure><ol><li>webpack 原理</li></ol><p>初始化阶段: - 初始化参数: 从配置文件或 shell 获取参数 - 创建编译器对象: 根据参数创建 compile 对象 - 初始化编译环境: 注入内置插件,rule 规则,加载的插件. - 开始编译: 执行 compile 的 run 方法 - 确定入口: 根据配置的 entry 找到入口,将入口文件转换为 dependence 对象</p><p>构建阶段: - 编译模块(make): 根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理. - 完成编译: 得到了每个模块被翻译后的内容以及它们之间的 依赖关系图</p><p>生成阶段: - 输出资源: 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表 - 写入文件系统(emitAssets): 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统.</p>]]></content>
<summary type="html">
<h1 id="webpack-配置指南"><a href="#webpack-配置指南" class="headerlink" title="webpack 配置指南"></a>webpack 配置指南</h1><ol>
<li>搭建环境</li>
</ol>
<figure
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="webpack 配置" scheme="http://yoursite.com/tags/webpack-%E9%85%8D%E7%BD%AE/"/>
</entry>
<entry>
<title>读<<国富论>></title>
<link href="http://yoursite.com/2021/05/05/%E8%AF%BB%3C%3C%E5%9B%BD%E5%AF%8C%E8%AE%BA%3E%3E/"/>
<id>http://yoursite.com/2021/05/05/读<<国富论>>/</id>
<published>2021-05-05T06:11:54.000Z</published>
<updated>2022-03-23T09:54:01.176Z</updated>
<content type="html"><![CDATA[<h1 id="国富论"><a href="#国富论" class="headerlink" title="国富论"></a>国富论</h1><h3 id="论劳动生产力改善的原因,并论劳动产出自然而然在各社会阶级间分配的次序"><a href="#论劳动生产力改善的原因,并论劳动产出自然而然在各社会阶级间分配的次序" class="headerlink" title="论劳动生产力改善的原因,并论劳动产出自然而然在各社会阶级间分配的次序"></a>论劳动生产力改善的原因,并论劳动产出自然而然在各社会阶级间分配的次序</h3><ol><li>论分工</li></ol><p>然而任何一种行业,若能引进分工,都会因分工而使劳动生产力得到相当比例的提高。而且不同行业与职业之所以相互分离出来,似乎也是由于分工有这种好处。一般来说,产业最发达进步的国家,通常也是分工程度最高的国家</p><p>我们不能将农业所运用的各种劳动完全清楚分开、相互独立出来,也许就是农业劳动生产力改善的速度跟不上制造业的主要原因。</p><p>贫国土地耕种的情况,尽管次于富国,但贫国生产的小麦,在质量和价格方面,却在相当程度上能与富国竞争;但在制造业方面,尤其是那些适宜富国土壤气候与位置的制造业,贫国最好别想和富国竞争。</p><p>分工之后,同样人数的工人所能生产的数量大为提高,主要是基于三种不同理由;第一,每个工人手脚灵巧的程度提高了;第二,工人不再需要从一种工作转换到另一种工作,节省了一些时间;第三,由于发明了许多机器,简化与节省了人力,使一个人能够完成许多人的工作。</p><p>目前那些分工最细密的制造业所使用的机器,大部分原本是某些普通工人的发明;他们每个人都只操作某种简单的工序,自然而然会把心思花在设法找出较简便的操作方式。</p><ol><li>论促成分工的原理</li></ol><p>分工的形成,是因为人性当中有某种以物易物的倾向;这种倾向的作用虽然是逐步而且缓慢的,也完全不问分工是否会产生广泛的效用;然而分工却是这种倾向必然产生的结果。</p><p>我们每天有吃有喝,并非由于肉商、酒商或面包商的仁心善行,而是由于他们关心自己的利益。</p><p>一如我们利用相互约定、交换或购买的方式,从他人身上取得绝大部分自己所需的帮忙,追根究底来看,导致目前这种分工状态的,也正是我们这种相互要求交换的倾向。</p><p>每个人都发觉有把握随时根据自己的需要,拿自己生产出来的剩余部分(即超出自己需要消费的部分),交换别人生产出来的剩余部分;人们就是因为有了这种确实的把握,才各自致力于某种特殊的行业,从而将每个人身上所有的才能或天分,发挥与磨练至最完美且适合各特殊行业的境界。</p><p>由于缺乏相互交换的能力与倾向,这些动物的不同天分与才能必然会产生的各种效果,不能集结在一起,因此对于整个种族的生活与便利,丝毫没有贡献,每只动物现在仍然必须个别独立仰赖自己与保卫自己。所以,虽然它们天生具有许多不同的才能,但是从彼此天生的差异中,它们得不到任何好处。人类的情况正好相反,极不相似的才能可以彼此帮忙;他们个别生产出来的不同物品,好像都被人类这种相互交换的倾向,集结在一起,让每个人可以根据自己的需要,随时在那里买到利用他人的才能生产的物品。</p><ol><li>分工受限于市场范围</li></ol><p>正因为交换的力量产生了分工,所以分工的程度必然受制这种力量的大小,或者说受制于市场的广狭。如果市场非常小,完全致力于一种行业的人,便得不到多少激励,因为他本身劳动产出的剩余部分,无法全部用来交换他人劳动产出的剩余部分。<br>由于每一种产业,通过水运都比单靠陆运接触的市场更加广大,所以每种产业自然在海滨或适宜航行的河流两旁,开始进行产业内分工与生产力改善;而且往往需要经过很长的一段时间,分工与改善的现象才会延伸至内陆地带。各种工艺与产业的改良也是如此.</p><ol><li>论金钱的起源与应用</li></ol><p>分工的势态一旦彻底确立,任何人本身劳动的产品,便只能供应自己极小部分的日常需要。他用来供应自己绝大部分需要的办法,是以本身劳动产出的剩余部分,向他人换得自己恰好需要的部分。于是每个人都得靠交易过活,或者说,都在一定程度内变成了商人,而整个社会也就真正变成了所谓的商业社会。</p><p>每一个审慎考虑将来的人,不管他活在哪种社会发展阶段,在有了初步的分工之后,自然都会努力安排自己的日常事务,使自己不管在什么时候,手上除了本业特殊的产品之外,还持有一定数量的特殊商品或物品;那些商品或物品,是他认为当自己需要用它们来交换他人的劳动产品时,很少有人会拒绝的东西。</p><p>然而,无论在哪个国家,人们似乎最后都基于无法抗拒的理由,而选择了金属,取代其他东西当做交易工具。几乎没有其他东西比金属更不易腐败,因此贮存期间比其他东西更长。金属不仅耐久保存,而且不管把它分割成几个部分,或者再把那几个部分融在一起,都不会造成损失。也正因为具有这种特性,所以金属才特别适合作为买卖与流通的工具.如此粗糙的金属当做金钱使用,会有两个不算小的麻烦。第一个麻烦发生在要称它们重量的时候;第二个麻烦发生在要评鉴成分的时候。</p><p>为了防杜这种诈欺恶习,也为了方便进行交易,以刺激工商业发展,那些相当文明进步的国家才觉得,需要将国内人民常用来购买东西的金属,选取若干定量,盖上一个象征公信力的戳印。铸币,以及那些称作铸币厂的政府机关就这样产生了</p><p>我相信,世界上每一个角落,都曾经有过贪婪与不义的君主或主权国家,滥用人民对它们的信任,一点一滴偷减钱币里起初含有的真正金属数量。</p><p>必须注意“价值”一词有两个不同的意思,有时它表示某一特别物品的效用;有时则表示该物品给予占有者购买其他物品的能力。前者也许可称之为“使用价值”,而后者或许可称之为“交换价值”。那些具有最大使用价值的物品,往往几乎或完全没有交换价值;相反的,那些具有最大交换价值的物品,却往往几乎或完全没有使用价值。没有什么东西比水更有用,可是水却几乎买不到任何东西。相反的,钻石几乎没有使用价值;但拿钻石去交换,往往可以得到大量的其他物品。</p><ol><li>论商品的真实价格(劳动价格)与名义价格(金钱价格)</li></ol><p>因此他是贫是富,就要看他能够支配或购买多少别人的劳动数量而定。对于任何商品的占有者来说,当他不打算自己使用或消费,只想用来交换其他有用的物品时,它的价值就等于能够用来购买或支配的劳动数量。所以劳动是测量一切商品交换价值的真正标准。世上所有的东西,追根究底都不是用金银买来的,而是用劳动取得的。对于任何物品的占有者来说,当他想用它交换某些新产品时,它的价值就等于它能购买或支配的劳动数量。</p><p>虽然劳动是测量一切商品交换价值的真正标准,但平常测量商品的价值,却无法使用这种标准。要确定两种不同劳动数量的比例,时常会有困难。于是事情慢慢演变成商品的交换价值,通常以金钱的数量来估计,而不以劳动或其他商品的数量来估计。虽然每一种商品,通过交换,最后都可换得劳动或其他商品。然而金银的价值总是会随着矿脉的蕴藏丰富与否而起伏不定。</p><p>同一数量劳动可买到的商品,有时较多,有时较少;但这是那些商品价值变动的结果,而不是因为购买那些商品的劳动价值起了变化。只有劳动本身的价值绝不会改变,所以,在任何时间与任何地点,只有劳动才是测量与比较一切商品价值的基本真实标准。以劳动单位表示的价格是商品的真实价格,而以金钱表示的只是商品的名义价格罢了。雇主购买劳动所付出的物品数量有时多有时少,所以在他看来,劳动价格有时贵有时便宜,也和其他商品一样会变动。实际上,那是商品有时便宜有时贵造成的结果。</p><p>在产业发展的过程中,商业化的国家发现,为了交易方便,需要同时利用几种金属铸造钱币。例如用金币支付大笔的金额,用银币购买中等价值的东西,用铜币或其他粗贱金属购买价值更小的物品。”“但是,那些国家总是认为,其中某一种钱币比其他两种更特别,而把它当做测量价值的标准。一般来说,被视为价值标准的金属,往往就是最先用作交易媒介的金属。<br>在所有的国家,法定的支付工具,起初一定只有一种钱币,而且正是用价值测量标准的金属铸成的。事实上,不管法律如何规定各种钱币金属的价值比例,只要规定没有更改,所有钱币的价值便会取决于最贵重金属的价值。</p><ol><li>论商品价格的构成部分</li></ol><p>在商品价格的构成部分中,利润与劳动工资完全不同,而两者也确实取决于截然不同的原则。在这种情形下,劳动的全部产出不一定完全属于劳动者本人。在大多数场合,他必须和雇主分享自己的劳动产出。通常取得或生产任何商品所需的劳动数量,不再是调节该商品应当购买、支配或换得多少劳动数量的唯一依据。任何商品显然都必须换得额外的劳动数量,才使垫付工资与提供材料的资本可以获得适当的利润。</p><p>无论在哪一个国家,一旦土地全部成为私有财产,像所有人类一样喜欢不劳而获的地主,便会开口索取地租. 需要注意的是劳动不仅是测量拆解成劳动所得这一部分商品价格的价值标准,也是测量拆解成地租和利润部分的价值标准。</p><p>在每一个社会里,任何商品的价格,最后可以拆解成(地租,工资,利润)这三种成分当中的某一种或两种,或全部三种。工资、利润与地租是一切收入,以及一切交换价值的三个根本来源。其他一切收入,追根究底,都源于这三种收入当中的某一种。不管是谁,想从自有资源得到收入,都只有三个办法:用自己的劳动、自己的资本,或自己的土地去取得。</p><p>当这三种收入分别属于不同人时,它们很容易分辨。可是,当它们属于同一个人时,则时常会被纠缠在一起。农夫不雇用他人帮忙而亲自辛苦耕种,如此节省下来的工资,其实就是他自己赚得的工资。所以说,农夫在这里便混淆了工资与利润。</p><p>但是,从来没有一个国家,能把每年的产出完全用在维持勤劳工人的生活。无论在什么地方,游手好闲的人总会消费掉社会每年产出的大部分。任何社会每年平常或平均产出的价值,究竟是年年增加,或年年减少,或呈现停滞状态,要视该社会的每年产出,在前述两种社会阶级间的分配比例而定。</p><ol><li>论商品的自然价格和市场价格</li></ol><p>当某种商品的价格,按照自然报酬率,不多不少刚好足够支付所有栽培、制作,直到上市的土地地租、劳动工资与资本利润时,那么该商品可说是按它的自然价格出售。对于任何商品来说,通常实际卖得的价格称为它的市场价格。这个价格也许高于、低于、或恰好等于它的自然价格。对于任何商品来说,市场价格的高低取决于两种数量间的比例,一是实际上市的商品数量,二是那些愿意支付自然价格的买者,他们需求的商品数量。</p><p>那些愿意支付自然价格的买者可称为有效需求者,而他们的需求数量可称为有效需求量,因为这种需求量,足以促使商品实际被带到市场来卖。它和绝对需求量不同。只要某一商品的上市数量超过有效需求量,则其价格当中某些成分的收入,便会低于它们的自然报酬率。相反的,一旦上市数量低于有效需求量,则商品价格当中某些成分的收入,便会高于它们的自然报酬率。</p><p>对任何商品来说,偶尔与暂时性的市价波动,在价格的诸构成要素之中,主要影响工资与利润的部分。地租部分虽然也受到影响,但是程度比较小。然而,究竟影响工资或利润,便要看市场当时发生存量过剩或不足的物品,究竟是商品或劳动。</p><ol><li>论劳动工资</li></ol><p>无论在什么地方,一般工资水平,取决于雇主和工人通常会达成的契约。 由于人数比较少,所以雇主们比较容易团结起来.此外,法律也允许雇主团结起来,至少不会禁止。然而,法律却禁止工人团结。</p><p>对工人的需求,必然都会跟随国家的财富而增加;如果国家的财富没有增加,对工人的需求便不可能增加。劳动工资之所以升高,不是因为一国拥有大量财富,而是因为一国的财富不断增加。因此,工资最高的国家,不是在最富有的国家,而是在最欣欣向荣,或者说财富成长最迅速的国家。</p><p>对任何国家来说,要知道它的繁荣程度,最简单可靠的办法,莫过于看人口增加的状况。</p><p>劳动获得宽裕的报酬,不仅是一国财富不断增加的必然结果,同时也是一国财富不断增加的自然症候。另一方面,贫穷的劳动阶级生活捉襟见肘,是一国财富停滞的自然症候,而该阶级人民濒临饿死,是一国财富迅速萎缩的自然症候。</p><p>对人口的需求,也像对其他任何商品的需求一样,就依照这种方式自然而然的调节人口的成长速度;当人口的成长进行得太慢时,超额需求会使它加速;当人口成长速度得太快时,需求不足会使它放慢。调节与决定世界各地人口成长速度的关键正是劳动需求。</p><p>绝大部分人民,处境最快乐也最舒服的时候,似乎是在社会不断进步,也就是社会持续累积财富的时候,而非已经取得了所有财富的时候。贫穷的劳动阶级,在社会停滞时,处境艰难;在社会退步时,处境悲惨。事实上,对所有社会各阶层来说,进步中的社会最令人感到欢乐。停滞的社会,令人感到单调无聊;退步的社会,令人忧郁感伤。</p><ol><li>论资本利润</li></ol><p>利润和工资的升降,取决于同一个原因,即是取决于社会财富增加或减少。</p><p>无论在哪一国,当寻常的市场利率发生变动时,就可以推定资本的平常利润也有同样的变动。利率降时,它也降,利率升时,它也升。</p><p>一般来说,在大城市经营任何行业,比在乡村需要更多的资本。城市里,除了各种行业都运用比较大量的资本外,有钱的竞争者人数也比较多,因此一般来说,城市的利润率会被压低到乡村的利润率以下。在每一个欣欣向荣的城市里,有大量资本可用的人,时常达不到他们想要雇用的劳工人数,所以会互相竞价争取工人,从而提高了工资,也降低了利润。在偏远的乡村地区,时常没有足够的资本可以雇用全部的劳工,所以劳工会互相竞价争取工作,从而降低了工资,也提高了利润。</p><p>当财富、各方面进步和人口增加之后,利率便会跟着下降。劳动工资不会跟随资本利润下降。不管资本的利润如何,只要资本增加,劳动需求就会跟着增加。</p><ol><li>论劳动与资本在不同行业的工资与利润</li></ol><p>行业本身性质不同所产生的工资差异:<br>一、工作本身讨人喜欢(工资低)或令人厌恶的程(工资高)度。<br>二、学得工作技巧的过程,是否既容易又便宜(工资低),或是既困难又昂贵(工资高)。<br>三、工作机会是否稳定(工资低),或是时有时无(工资高)。<br>四、执行工作的人是否必须特别值得信赖(工资高),或是一般人(工资低)即可。<br>五、行业经营(或职业生涯)获得成功的几率大(工资低)小(工资高)。</p><p>在前述五个影响劳动工资的因素当中,只有两个因素对资本利润有影响,也就是行业本身讨人喜欢或令人厌恶的程度,以及投资获利风险或安全的程度.</p><p>在小城镇或乡村地区,由于市场狭小,即使扩大应用资本,生意不见得一定跟着扩大。因此,在这种地方,尽管业者的利润率很高,利润总额却不可能很大,从而每年累积的资本也不可能很多。相反的,在都市地区,生意随着资本增加而扩大,而且生活节俭、事业蒸蒸日上的生意人能够获得的信用,比他自己的资本增加得更快。他的生意按资本和信用增加的比例扩大,利润总额按生意扩大的比例增加,每年累积的资本按利润总额增加的比例提高。</p><p>政策导致的差异:<br>一、限制加入某些行业竞争的人数,使它小于自由的情况下愿意加入的人数。<br>二、增加某些行业的就业人数,使它超过自由的情况下愿意加入的人数;<br>三、阻挠劳动和资本在不同行业之间以及不同区域之间的自由流通。</p><ol><li>论地租</li></ol><p>略</p><h3 id="论物品积蓄的性质、累积与运用"><a href="#论物品积蓄的性质、累积与运用" class="headerlink" title="论物品积蓄的性质、累积与运用"></a>论物品积蓄的性质、累积与运用</h3><p>一旦社会分工彻底实施了以后,每个人随时需要的各种物品,他自己的劳动产出只能供应极小的一部分。只能用自己的劳动产品价格购买。不过,这个购买动作,必须等到自己的劳动产品不仅已经完成。因此,必须有一定数量的各种物品事先积蓄在社会的某个角落,至少足够维持个人的生活.</p><p>如果说物品积蓄的累积必须在分工之前,否则劳动生产力便不会有重大的进步,那么我们同样可以说,物品积蓄的累积自然会导致生产力进步。这种积蓄累积成各种不同的资本.</p><ol><li>论物品积蓄(资本)的种类</li></ol><p>当某个人拥有的物品积蓄仅仅足够维持几天或几个礼拜的生活时,他不太会想到利用这项积蓄来衍生任何收入。可是当他拥有的物品积蓄足够维持几个月或几年的生活时,他便自然会尽力利用大部分的积蓄来衍生收入,而只保留一小部分的积蓄,所以,他的全部积蓄可区分成两部分。其中他期望衍生收入的部分,可以说是他的资本。另外一部分则是直接供应他消费使用的积蓄。</p><p>资本有两种不同的运用方式,可以让运用它的人获得收入或利润。一是流动资本(卖出前的商品),二是固定资本(土地,机器).</p><p>一个国家或社会全部的物品积蓄,等于所有居民或成员物品积蓄的总和,因此它也自然分成同样的三部分.第一部分是留作直接消费使用的部分,特征是它不会提供收入或利润。第二项就是固定资本,它的特征是毋须循环或易主,便可以提供收入或利润。最后,也是第三项,就是流动资本。它的特征是,唯有通过循环或来回易主,才可能提供收入。</p><p>固定资本和流动资本两者最终和唯一目的,就是要维持和增加留作直接消费使用的物品积蓄。正是后面这部分物品积蓄让人们有得吃、有得穿、有得住。人民的贫富取决于这两种资本提供了多少物品,可以让人民保留,当作直接消费使用的积蓄。</p><ol><li>论货币作为社会全部积蓄中的一个特殊种类或论国家资本的维持费用</li></ol><p>就每个国家全部土地与劳动每年生产的所有商品总和来看,也必然如此。换句话说,每年产出的全部价格或交换价值,必定自然同样分解成这三种成分,以劳动工资、资本利润或土地的租金等三种形式,分配给该国各阶层的居民。</p><p>虽然固定资本全部的维持费必须从社会的净收入中剔除,但是,流动资本全部的维持费却不一样。流动资本包含四个部分:钱币、食物、原材料和制成品。前文已经指出,后面三项经常从流动资本中抽离出来,成为社会的固定资本,或是成为人们留在身边供作直接消费使用的物品积蓄。在社会全部的流动资本当中,唯有钱币这部分的维持费,才会导致社会净收入有所减少。</p><p>我们虽然通常是以每年支付给某人的钱币数量来表示他的收入,这是因为这些钱币数量会影响他的购买力大小,或影响他每年能够消费价值多少的物品。但是,我们仍然会认为,他的收入在于购买力或消费能力,而不在于传送购买力的钱币。</p><p>自由竞争也使得每一个银行家和客户打交道时更为慷慨大方,唯恐客户会被其他竞争对手抢走。一般说来,任何一种行业或任何一种劳动部门,只要是对大众有利的,那么竞争愈是自由并且愈为普遍,对大众就愈为有利。</p><ol><li>论资本的累积,兼论生产性和非生产性劳动</li></ol><p>有一种劳动施加在物品上,会增加物品的价值,另外有一种劳动没有这种效果。前者或许可以称为“生产性劳动”,因为它会产生价值。相对的,后者可以称为“非生产性劳动”。一般来说,制造工人的劳动,在他加工的材料上,增加了一部分价值,可以提供本身生活所需和雇主利润。相反的,侍奉主人的奴仆不会增加任何东西的价值。</p><p>制造的物品可以在日后有需要的时候,拿来驱动某一数量的劳动工作,这个数量和当初生产该物品的劳动相等。相反的,侍奉主人的奴仆,他的劳动并不附加或体现在任何物品.</p><p>一国的产出,不管数量多大,绝不可能是无穷无尽的,而是一定会有某个限度。所以,在任何一年内,如果用比较多的产出去维持非生产性劳动者,剩下来维持生产性劳动者的产出就会比较少,从而来年的产出也会比较少。相反的,维持生产性劳动者的产出比较多,来年的产出也会比较多。</p><p>勤劳与懒惰的比例高低,不管在什么地方,似乎都随资本与收入的相对比例而起伏。资本比例高的地方,勤劳的比例也高;收入比例高的地方,懒惰的比例也高。因此,资本的增加或减少,自然会倾向增加或减少勤劳的实际数量,增加或减少生产性人员的数目,从而增加或减少一国土地和劳动每年产出的交换价值。也就是说,增加或减少该国所有居民的实质财富和收入。</p><p>资本因节俭而增加,因浪费和错误运用而减少。由于节俭扩大了准备用来维持生产性人员的财源,所以节俭有助于提高生产性人员的数目。而生产性人员的劳动,则会使施工的物品价值增加。所以节俭有助于增加一国土地和劳动每年产出的交换价值。它驱动了额外数量的勤劳,而这额外的勤劳将增添每年产出的价值。资本增加的直接原因是节俭,而非勤劳。不错,勤劳提供物品让节俭得以累积。但是,不管勤劳可以取得多少物品,如果没有节俭来储蓄或贮存,资本便不可能增加。</p><p>当国内的产出价值减少时,在国内流通的消费品价值必然也会跟着减少,从而需要用来流通消费品的钱币数量也会跟着减少。</p><ol><li>论贷出取息的积蓄</li></ol><p>贷出取息的积蓄,有时候确实会被用来供应日常的生活消费,但实际上更常被当作资本使用。借钱来消费的人,很快会变得一贫如洗,以致借钱给他的人,终将因自己当初的愚蠢而懊悔不已。</p><p>我们可以这样看待贷出取息的资本:贷款者将相当可观的一份产出转让给借款者使用,回报的条件之一是,在借款存续期间内,借款者必须每年将比较小的一份产出转让给贷款者,比较小的这一份产出称作利息;条件之二是,在借款期满时,借款者必须将一份大小和当初转让给他的那一份相等的产出转让给贷款者,这一份产出称作还本。货币借贷的利息,它总是和资本的利润同步升降.</p><ol><li>论资本的各种用途</li></ol><p>资本有四种不同的用途:第一,生产社会每年需要使用和消费的初级产品。第二,对这些初级产品进行加工制造,供应直接使用和消费。第三,将初级产品或制成品,从供给充裕的地方运送到供给不足的地方。第四,将一部分初级产品或制成品,分装成方便人们偶尔需要使用的数量单位。所有从事土地改良或耕作、采矿和渔捞的资本,全都属于第一种用途。各种制造业主的资本,全都属于第二种用途;各种批发商的资本,全都属于第三种用途。各种零售商的资本,全都属于第四种用途。</p><p>同一数量的资本用于国内贸易,通常会比用于外国消费品贸易,激励和支持更多的本国生产性劳动,同时增加更大的本国产出价值。而同一数量的资本用于外国消费品贸易,又比用于海外贩运贸易,更有利于本国生产性劳动和产出价值的提升。每一个国家的财富,以及只要国家的力量取决于财富,必然和最后支付一切税收的财源成正比。增加本国的财富和国力,几乎是每个国家经济政策的主要目的。</p><p>当一国的资本积蓄,增加到超过供应本国消费和支持本国生产性劳动全部所需的数量时,多余的资本自然会流向海外贩运贸易,在他国发挥同样的功能。海外贩运贸易是一国财富极大的自然结果和象征,但似乎不是一国致富的自然原因。</p><h3 id="论不同国家财富增加的过程"><a href="#论不同国家财富增加的过程" class="headerlink" title="论不同国家财富增加的过程"></a>论不同国家财富增加的过程</h3><ol><li>论国家财富增加的自然过程</li></ol><p>城市和乡村的交易其实是互助互惠的,两者之间的劳动分工,就像其他所有的分工那样,对所有参与分工、从事各种不同职业的人,都是有利的。城市居民的数目和收入愈多,乡村居民在那里享有的市场规模便愈大;而那里的市场规模愈大,总是对大多数人都愈有利.</p><p>如果各种人为扭曲的制度从未骚扰自然的事态发展,那么在每个国家,城镇财富和人口的发展,不仅在时间上会较晚,而且在规模上也会受限于周围土地或乡村的改良和耕种发展。</p><p>按照自然的道理,每个成长中的社会都将大部分资本优先投入农业,然后是制造业,最后才是国外贸易业。虽然每个成长中的社会,都必然在某个程度内有这种自然的发展次序;然而在许多方面,欧洲所有的现代国家,它们的发展次序却完全颠倒过来。这些国家的某些城市率先经营国外贸易,然后引进各种适合销售到远方的精致制造业;然后这些制造业再和国外贸易业,一起形成农业方面的重大进步。这些国家原来的政府所建立的各种风俗习惯,在政府经历了很大的变革之后,仍然被保留下来;正是碍于这些风俗习惯,所以这些国家的财富发展才会有这样不自然的相反顺序。</p><ol><li>论罗马帝国灭亡后欧洲古代国家农业发展的阻碍</li></ol><p>古罗马人就是遵行这种自然的继承法。他们在土地继承上对于长幼和性别的区分,不会比我们现在分配动产时分得更仔细。但是,当土地被认为不仅是提供食物的手段,更是掌握权力和保障生命财产的重要手段时,人们便觉得土地最好只传给一个人,不要分割。因此,一处地产的安全,或地产的主人对于地产上的居民所能提供的保障,便取决于地产的大小。要是把土地分割,便等于是要让它毁灭。因为成本的原因,这种庞大的地产对土地改良很不利。很难指望大地主进行耕种改良,那么,更不能指望他辖下的承租人进行耕种改良.</p><p>逐渐取代这种佃农的,是真正配称为农夫的耕种者。他们运用自己的积蓄耕种土地,支付一定的地租给地主。当这种农夫握有期限长达好几年的土地租约时,他们有时候会发现,拿出自己部分的资本进一步改良土地,对他们自己有利。因为他们有时候可以在土地租约到期以前将投资完全收回来,同时还可以获得可观的利润.当然最重要的是制定法律排除任何一种土地继承人的影响,以保障土地租约享有最长的时效.</p><ol><li>论罗马帝国灭亡后城镇的兴起和进步</li></ol><p>彼此的利益便使得镇民倾向支持国王,也使得国王倾向支持镇民抵抗封建领主。镇民是国王敌人的敌人,所以尽可能让镇民安全独立,避免他们受到共同敌人的挟持,便是国王的利益所在。国王准许镇民选任自己的行政和司法长官,让他们有权订定内规治理自己内部的事务,有权建筑城墙保卫自己,以及有权迫使所有居民服从某种军事纪律,等于是把国王能够授与的手段全部都给了镇民,让他们用来对抗封建领主,保护自己的安全和独立。</p><p>在某些国家,譬如像意大利和瑞士,或许是由于距离中央政府的主要根据地太过遥远,也或许是由于乡村地区本身自然的力量,或者由于某些其他原因,君主逐渐完全丧失了权威。在这样的国家里,各个城市通常变成了独立的共和国,并且征服了所有邻近的诸侯贵族,迫使诸侯拆除在乡下的城堡,到城市里与禀性和平善良的居民住在一起。十二世纪末至十六世纪初,这种共和国兴起和灭亡的数目非常多。</p><p>社会秩序和优良政府,以及随之而来的个人自由和安全,就这样在各个城市里确立起来。逃出来的农奴,如果能够在城镇里躲上一年,而不被他的主人逮到,那么他便永远自由了。因此,凡是在勤劳的乡村居民手中累积起来的积蓄,都自然会逃到城市.</p><p>十字军东征,虽然浪费了大量的社会积蓄,也摧毁了无数生命,必定减缓了欧洲大部分地方的进步。然而,十字军东征对意大利的某些城市极为有利。从欧洲各地出发征讨巴勒斯坦圣地的各路大军,让威尼斯、热那亚和比萨的船运业生意兴隆。商业城市的居民,可以从比较富有的国家进口各种改良的制品和昂贵的奢侈品,来满足大地主们的虚荣心。对各种比较精致进步制品的嗜好,就这样经由国外贸易,导入了各个从来没有做过这种制品的国家。当这种嗜好变得相当普遍,以致市场的需求很大时,商人为了节省运输费用,自然会尽力在自己的国家设立同一种类的制造业。这似乎是罗马帝国灭亡后,西欧诸省开始以远方为销售市场而建立各种制造业的由来.</p><ol><li>论城镇商业活动如何促进乡村改良</li></ol><p>商业和制造业城市的繁荣,以三种不同的方式,对城市所属国家的乡村改良和农业进步,产生正面的影响。<br>第一,城市为乡村地区的初级产物提供了广大和现成的市场,鼓励乡村地区的农业耕作和土地改良。<br>第二,城市的居民累积了财富以后,往往会在乡村地区购买一些等待出售的土地,这种土地多半无人耕种。商人通常有强烈的企图心,希望变成乡绅,提高自己的社会地位,而当他们得遂所愿以后,往往成为最佳的土地改良者。<br>第三,也是最后一种方式,商业各种精致先进的制造业,逐渐把社会秩序和优良政府,以及随之而来的个人自由和安全导入乡村地区。</p><h3 id="论政治经济学的思想体系"><a href="#论政治经济学的思想体系" class="headerlink" title="论政治经济学的思想体系"></a>论政治经济学的思想体系</h3><p>略</p><h3 id="论君主或国家收入"><a href="#论君主或国家收入" class="headerlink" title="论君主或国家收入"></a>论君主或国家收入</h3><p>略</p>]]></content>
<summary type="html">
<h1 id="国富论"><a href="#国富论" class="headerlink" title="国富论"></a>国富论</h1><h3 id="论劳动生产力改善的原因,并论劳动产出自然而然在各社会阶级间分配的次序"><a href="#论劳动生产力改善的原因,并论劳
</summary>
<category term="读书" scheme="http://yoursite.com/categories/%E8%AF%BB%E4%B9%A6/"/>
<category term="读书 笔记" scheme="http://yoursite.com/tags/%E8%AF%BB%E4%B9%A6-%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>Vite 使用教程</title>
<link href="http://yoursite.com/2021/02/22/Vite%20%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B%20/"/>
<id>http://yoursite.com/2021/02/22/Vite 使用教程 /</id>
<published>2021-02-22T05:39:21.000Z</published>
<updated>2021-08-30T09:54:36.241Z</updated>
<content type="html"><![CDATA[<h1 id="vite-教程"><a href="#vite-教程" class="headerlink" title="vite 教程"></a>vite 教程</h1><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm init @vitejs/app <span class="comment"># npm 安装</span></span><br><span class="line">yatn create @vitejs/app <span class="comment"># yarn 安装</span></span><br></pre></td></tr></table></figure><h3 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h3><ol><li><p>热启动快</p></li><li><p>模块热重载<br>vite 提供一套原生 ESM 的 HMR(即时热更新 hot module replacement) API 实现热重载,无需重新加载页面和应用.</p></li><li><p>按需编译</p></li></ol><p>Vite 要求项目完全由 es module 模块组成,因此不能再生产环境使用,打包依旧由 rollup 打包工具,目前vite 更像是一个类似于 webpack-dev-server 的开发工具.</p><h3 id="es-module-和-commonJs"><a href="#es-module-和-commonJs" class="headerlink" title="es module 和 commonJs"></a>es module 和 commonJs</h3><p>es modules 是浏览器支持的一种模块化方案.<br><code>import HelloWorld from './Helloworld.vue'</code>,当浏览器解析这条语句的时候会往当前域名发送一个请求获取对应的资源.<br>我们平时在 webpack 上写的 esmodule 代码会被打包成 commonJs 的方式运行,所以运行速度较慢.<br>目前 90% 的浏览器都已支持 基于 web 标准的 es module.<br>浏览器对于带有type=”module”的<code><script></code>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<code><script></code>标签的defer属性。有多个该标签会依次执行.</p><p>CommonJs 是 NodeJs 中的规范,每个模块都有一个 exports 私有变量,exports 指向 module.exports.require 命令可以读入并执行一个 js 文件然后返回该文件的 exports 对象.require 被导出的值的拷贝.commonJs 是动态分析,动态加载.先整体加载模块,再从对象上读取里面的方法,因为只有运行中才能得到对象所以没有办法在静态编译时做静态优化.</p><p>esModule 的导入和导出都是声明式的,必须位于模块的顶层作用域,在 es6 代码编译阶段就可以分析模块依赖.通过静态分析未被调用的模块不会被执行和打包,确保模块之间传递的值和接口类型正确.import 导入的模块是只读的,不允许在导入后直接对其修改.如果是一个对象,可以对对象的属性进行修改.由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。</p><p>不同: cjs 输出的是值拷贝,mjs 输出的是值引用. cjs 是运行时加载,mjs 是编译时记载.cjs 中 this 指向顶层对象,mjs 指向 undefined.</p><p>esModule 如果想要动态记载,可以使用 2020 提案中的 import()函数.该函数的参数可以是动态的.与 commonJS 中 require 的区别就是 require 是异步加载,import()是同步加载.加载成功后使用 then 方法从参数中获取模块.</p><p>node 要求 es6 模块使用 mjs 后缀,如果不想改后缀需要在 package.json 中加 <code>'type' : 'module'</code>,这时 commonjs 需要加 cjs 后缀了.两者尽量不要混用.</p><p>esModule 加载路径必须给出脚本的完整路径,不能省略后缀名. 而使用webpack时 由于在<code>resolve.extensions: ['.js','.jsx','.vue']</code>配置过,可省略后缀名.而vite可以通过<code>resolve.extensions :['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']</code>配置.</p><h3 id="webpack-VS-vite"><a href="#webpack-VS-vite" class="headerlink" title="webpack VS vite"></a>webpack VS vite</h3><p>我们经常会遇到我们改动一小行代码,webpack 会耗时数秒来重新打包.因为 webpack 需要将所有模块打包成一个或多个模块.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// b.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> b = <span class="number">2</span>;</span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> {a} <span class="keyword">from</span> <span class="string">'a.js'</span>;</span><br><span class="line"><span class="keyword">import</span> {b} <span class="keyword">from</span> <span class="string">'b.js'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> getNumber = <span class="function"><span class="params">()</span> =></span>{</span><br><span class="line"><span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 打包成 bundle.js</span></span><br><span class="line"><span class="keyword">const</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">const</span> b = <span class="number">2</span>;</span><br><span class="line"><span class="keyword">const</span> getNumber = <span class="function"><span class="params">()</span> =></span>{</span><br><span class="line"><span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">export</span> {getNumber};</span><br></pre></td></tr></table></figure><p>当我们修改一个子模块 bJs,整个 bundleJs 都需要重新打包.随着项目增大,重新打包的时间越来越长.热更新的速度越来越慢.<br>webpack 之所以慢,是因为 webpack 会将很多资源构成一个或多个 bundle.如果跳过打包过程,当需要某个模块的时候再通过请求去获取就完美的解决了这个问题.Vite 做到了.</p><h3 id="Vite-实现原理"><a href="#Vite-实现原理" class="headerlink" title="Vite 实现原理"></a>Vite 实现原理</h3><ol><li>请求拦截.<br>Vite 的基本原理就是启动一个 node 服务器拦截浏览器请求 es module 的请求,通过 path 找到对应文件做一定的处理,然后以 es module 的方式返回给浏览器.</li></ol><ol><li><p>esbuild.<br>Vite 对 js/ts 的处理没有经过 gulp/rollup 等传统打包工具,而是使用 esbuild,esbuild 是一个全新的 js 打包工具,支持如 babel,压缩等功能,它要比 rollup 等传统工具快上几十倍.原因是它使用了 go 语言作为底层语言.</p></li><li><p>node_modules 模块的处理<br>当我们在日常开发时引用 node_modules 的时候,我们会这样引用.<code>import vue from 'vue';</code>然后 webpack 等打包工具会帮我们找到模块的路径.<br>但是浏览器只能通过相对路径去找,vite 为了解决这个问题,对其做了一些特殊处理.当浏览器请求<code>vue.js</code>时,请求路径是<code>@modules/vue.js</code>.在 vite 中约定若 path 的请求路径满足<code>/^\/@modules\//</code>格式时,就被认为是 node_modules 模块.那么如何将代码中的<code>vue.js</code>变为<code>/@modules/vue</code> 呢? Vite 对 es module 形式的 js 文件模块处理使用了 Es module lexer 处理, Lexer(词法分析器)会返回 js 文件中导入模块以数组形式返回.然后通过该数组判断是否为一个 node_modules 模块,若是则重写其 path. 然后当浏览器发生 path 为<code>/@modules/xx</code>的对应请求时,会被 Vite 服务端做一层拦截,最终找到对应模块进行返回.<br>Vite 对 script 标签导入的模块也会有对应的处理.</p><ol><li><p>vue 文件的处理<br>当 Vite 遇到 vue 文件时,它会被拆分成 template,css,script 三个模块进行处理.最后会对 script,template,css 发送多个请求获取.比如 App.vue?type=template 获取 template,type=style 获取 css.</p></li><li><p>静态文件的加载<br>当请求的路径符合 imageRE,mediaRE,fontsRE 或 JSON 格式时会被认作静态资源.然后处理成 es module 模块返回.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/node/utils/pathUtils.ts</span></span><br><span class="line"><span class="keyword">const</span> imageRE = <span class="regexp">/\.(png|jpe?g|gif|svg|ico|webp)(\?.*)?$/</span></span><br><span class="line"><span class="keyword">const</span> mediaRE = <span class="regexp">/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/</span></span><br><span class="line"><span class="keyword">const</span> fontsRE = <span class="regexp">/\.(woff2?|eot|ttf|otf)(\?.*)?$/i</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isStaticAsset = <span class="function">(<span class="params">file: string</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> imageRE.test(file) || mediaRE.test(file) || fontsRE.test(file)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>HMR(即时热更新 hot module replacement)的原理<br>Vite 的热更新原理就是在客户端和服务端建立了一个 websocket 链接,当代码修改时服务端发送消息通知客户端重新请求信代码,完成更新.</p></li></ol><ul><li>服务端原理 websocket</li><li>客户端原理<br>vite 在处理 html 时写入 websocket相关代码.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> clientPublicPath = <span class="string">`/vite/client`</span></span><br><span class="line"><span class="keyword">const</span> devInjectionCode = <span class="string">`\n<script type="module">import "<span class="subst">${clientPublicPath}</span>"</script>\n`</span></span><br><span class="line"> <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">rewriteHtml</span>(<span class="params">importer: string, html: string</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> injectScriptToHtml(html, devInjectionCode)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// </span></span><br><span class="line"> <span class="comment">// Listen for messages</span></span><br><span class="line">socket.addEventListener(<span class="string">'message'</span>, <span class="keyword">async</span> ({ data }) => {</span><br><span class="line"> <span class="keyword">const</span> payload = <span class="built_in">JSON</span>.parse(data) <span class="keyword">as</span> HMRPayload | MultiUpdatePayload</span><br><span class="line"> <span class="keyword">if</span> (payload.type === <span class="string">'multi'</span>) {</span><br><span class="line"> payload.updates.forEach(handleMessage)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> handleMessage(payload)</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">handleMessage</span>(<span class="params">payload: HMRPayload</span>) </span>{</span><br><span class="line"><span class="keyword">const</span> { path, changeSrcPath, timestamp } = payload <span class="keyword">as</span> UpdatePayload</span><br><span class="line"> <span class="built_in">console</span>.log(path)</span><br><span class="line"> <span class="keyword">switch</span> (payload.type) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'vue-reload'</span>:</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="comment">// ....</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul></li></ol><h3 id="Vite-的一些优化"><a href="#Vite-的一些优化" class="headerlink" title="Vite 的一些优化"></a>Vite 的一些优化</h3><p>es module 如果包含相互依赖的话,页面初始化会发送大量请求. Vite 为了优化这个问题,给了一个 optimize 指令.它类似于 webpack 的 dll-plugin 插件,提前将 package.json 中的依赖打包成一个 esmodule 模块,这样在页面初始化就能减少大量请求.</p><p>Vite 只是一个用于开发环境的工具,上线仍会打包成一个 commonJs 文件进行调用.</p>]]></content>
<summary type="html">
<h1 id="vite-教程"><a href="#vite-教程" class="headerlink" title="vite 教程"></a>vite 教程</h1><h3 id="安装"><a href="#安装" class="headerlink" title="安
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="Vite" scheme="http://yoursite.com/tags/Vite/"/>
</entry>
<entry>
<title>Vue3 迁移指南</title>
<link href="http://yoursite.com/2020/09/29/Vue3%20%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97/"/>
<id>http://yoursite.com/2020/09/29/Vue3 迁移指南/</id>
<published>2020-09-29T05:42:05.000Z</published>
<updated>2021-08-30T09:57:27.756Z</updated>
<content type="html"><![CDATA[<h3 id="vue3-0-迁移指南"><a href="#vue3-0-迁移指南" class="headerlink" title="vue3.0 迁移指南"></a>vue3.0 迁移指南</h3><ol><li>全局 api 更改为 应用程序实例.<br>vue2.0 有很多全局的 api 和配置,比如 Vue.component 创建全局组件,Vue.directive 创建全局指令,Vue.mixins 和 Vue.use 等等..<br>因为Vue2.0 通过 new Vue(…)来创建根 Vue 实例,从同一个 Vue 构造函数创建的根实例共享相同的全局配置.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这会影响两个根实例</span></span><br><span class="line">Vue.mixin({</span><br><span class="line"> <span class="comment">/* ... */</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app1 = <span class="keyword">new</span> Vue({ <span class="attr">el</span>: <span class="string">'#app-1'</span> })</span><br><span class="line"><span class="keyword">const</span> app2 = <span class="keyword">new</span> Vue({ <span class="attr">el</span>: <span class="string">'#app-2'</span> })</span><br></pre></td></tr></table></figure></li></ol><p>vue3.0 提供了 一个全新的全局 API - creatApp,调用它返回一个应用实例.应用实例拥有当前全局 API 的子集.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { createApp } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">const</span> app = createApp({})</span><br><span class="line"><span class="comment">// app.component,app.directive,app.mixin,app.use...</span></span><br><span class="line">app.mount(<span class="string">'#app'</span>);</span><br></pre></td></tr></table></figure></p><ol><li>全局和内部 api 以及重构为可 tree-shaking (删除无用代码,不打包到 bundle)<br>vue2.0 时再用 Vue.nextTick() 或它的简单包装形式$nextTick()时,webpack 的 tree-shaking 不可摇动.<br>Vue3.0 对全局和内部 api 进行了重构,考虑到 tree-shaking 的支持,全局 api 现在只能作为 es 模块侯建的命名导出进行访问.例如:<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> {nextTick} <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line">nextTick(...);</span><br></pre></td></tr></table></figure></li></ol><p>受影响的 api 有:</p><ul><li>Vue.nextTick</li><li>VUe.observable(用 Vue.reactive 替代)</li><li>Vue.version</li><li>Vue.compile</li><li>Vue.set</li><li>Vue.delete</li></ul><ol><li>v-model 用法更改<br>在 vue2.0 中,v-model 用来双向绑定数据,但一个组件只能用于一个 v-model,如果需要多个双向绑定只能用.sync.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 2.x</span></span><br><span class="line"><ChildComponent :title=<span class="string">"pageTitle"</span> @update:title=<span class="string">"pageTitle = $event"</span> /></span><br><span class="line"><span class="comment">// 简写</span></span><br><span class="line"><ChildComponent :title.sync=<span class="string">"pageTitle"</span> /></span><br><span class="line"><span class="comment">// 子组件内部触发</span></span><br><span class="line"><span class="keyword">this</span>.$emit(<span class="string">'update:title'</span>, newValue)</span><br></pre></td></tr></table></figure></li></ol><p>在 vue3.0 中,v-model 通过后面要绑定的属性名来实现绑定多个值.<br>原理: vue2.0 的 v-model 通过绑定一个 value 属性和 input 事件,将输入e.target.value映射到 绑定的变量 值上.<br>而 vue3.0 相当于传递了modelValue 的 prop 并接收了抛出的 update 事件.<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">// 用在组件上</span><br><span class="line"><span class="tag"><<span class="name">custom-input</span> <span class="attr">v-model</span>=<span class="string">"searchText"</span>></span><span class="tag"></<span class="name">custom-input</span>></span></span><br><span class="line">// 组件内部的 input 必须将属性绑定在 modelValue 上且 事件触发通过 update:modelValue 抛出.</span><br><span class="line">app.component('custom-input', {</span><br><span class="line"> props: ['modelValue'],</span><br><span class="line"> template: `</span><br><span class="line"> <span class="tag"><<span class="name">input</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:value</span>=<span class="string">"modelValue"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">input</span>=<span class="string">"$emit('update:modelValue', $event.target.value)"</span></span></span><br><span class="line"><span class="tag"> ></span></span><br><span class="line"> `</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><ol><li><p>key 属性用法更改<br>对于 v-if,v-else,v-else-if 的 key 不再必须,Vue3.0 会自动生成 唯一的key,不建议手动赋予 key 值.<br>vue2.0 中 template 标签不能有 key 值,通常在它的子节点设置 key.在 Vue3.0 中,key 值应该设置在 template 标签中.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- Vue 2.x --></span></span><br><span class="line"><span class="tag"><<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"item in list"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-if</span>=<span class="string">"item.isVisible"</span> <span class="attr">:key</span>=<span class="string">"item.id"</span>></span>...<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">v-else</span> <span class="attr">:key</span>=<span class="string">"item.id"</span>></span>...<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- Vue 3.x --></span></span><br><span class="line"><span class="tag"><<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"item in list"</span> <span class="attr">:key</span>=<span class="string">"item.id"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-if</span>=<span class="string">"item.isVisible"</span>></span>...<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">v-else</span>></span>...<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br></pre></td></tr></table></figure></li><li><p>v-if 和 v-for 优先级调整.<br>vue2.0 中,v-for 的优先级最高.而在 Vue3.0 中,v-if 的优先级最高. 但都建议避免他们在同一元素上使用.</p></li><li><p>v-for 中的 ref 数组.<br>vue2.0 中 v-for 使用 ref 会用 ref 数组填充相应的 $refs.当 v-for 存在嵌套 v-for 时,这是不明确和效率低下的.<br>Vue3.0 中.这样的用法将不再自动创建数组,需要将 ref 绑定到一个灵活地函数上.</p></li><li><p>只能使用普通函数创建功能组件(函数式组件).<br>vue2.0 函数式组件示例:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">// Vue 2 函数式组件示例</span><br><span class="line">export default {</span><br><span class="line"> functional: true,</span><br><span class="line"> props: ['level'],</span><br><span class="line"> render(h, { props, data, children }) {</span><br><span class="line"> return h(`h${props.level}`, data, children)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">// 或者</span><br><span class="line"><template functional></span><br><span class="line"> <component</span><br><span class="line"> :is="`h${props.level}`"</span><br><span class="line"> v-bind="attrs"</span><br><span class="line"> v-on="listeners"</span><br><span class="line"> /></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">export default {</span><br><span class="line"> props: ['level']</span><br><span class="line">}</span><br><span class="line"></script></span><br></pre></td></tr></table></figure></li></ol><p>Vue3.0 不需要定义 functional,接收两个参数,props 和 context.(同 setup)<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { h } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">const</span> DynamicHeading = <span class="function">(<span class="params">props, context</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> h(<span class="string">`h<span class="subst">${props.level}</span>`</span>, context.attrs, context.slots)</span><br><span class="line">}</span><br><span class="line">DynamicHeading.props = [<span class="string">'level'</span>]</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> DynamicHeading</span><br></pre></td></tr></table></figure></p><ol><li>异步组件<br>Vue2.0 通过将组件定义为返回 promise 的函数来创建的.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> asyncPage = <span class="function"><span class="params">()</span> =></span> <span class="keyword">import</span>(<span class="string">'./NextPage.vue'</span>)</span><br></pre></td></tr></table></figure></li></ol><p>Vue3.0 由于函数式组件被定义为纯函数,因此异步组件需要包装在新的 defineAsyncComponent 方法显示定义.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { defineAsyncComponent } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">const</span> asyncPage = defineAsyncComponent(<span class="function"><span class="params">()</span> =></span> <span class="keyword">import</span>(<span class="string">'./NextPage.vue'</span>))</span><br></pre></td></tr></table></figure></p><ol><li><p>h 渲染函数更改.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 2.x 语法 render 函数接收 h 之类的参数</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> render(h) {</span><br><span class="line"> <span class="keyword">return</span> h(<span class="string">'div'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 3.x 语法 h 函数全局导入,不作为参数传递,可以用作 setup 的返回值函数</span></span><br><span class="line"><span class="keyword">import</span> { h } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> h(<span class="string">'div'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>slot 统一<br>在 3.x 中,将所有 this.$scopedSlots 替换为 this.$slots</p></li><li><p>自定义指令<br>自定义指令的钩子函数更改为与组件声明周期统一的事件钩子.<br>bind => beforeMount, inserted => mounted, 新增 beforeUpdate, update 与 componentUpdated => updated, 新增 beforeUnmounte, unbind => unmounted.</p></li><li><p>watch 和 $watch 不再支持.分隔符字符串路径,请改为计算函数作为参数.</p></li><li><p>destroyed 重命名为 unmounted, beforeDestroy 重命名为 beforeUnmount</p></li><li><p>prop 默认值函数中不再能访问 this,可以把组件接收到的原始 prop 作为参数传递给默认函数.或使用 inject.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { inject } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> props: {</span><br><span class="line"> theme: {</span><br><span class="line"> <span class="keyword">default</span> (props) {</span><br><span class="line"> <span class="comment">// `props` 是传递给组件的原始值。</span></span><br><span class="line"> <span class="comment">// 在任何类型/默认强制转换之前</span></span><br><span class="line"> <span class="comment">// 也可以使用 `inject` 来访问注入的 property</span></span><br><span class="line"> <span class="keyword">return</span> inject(<span class="string">'theme'</span>, <span class="string">'default-theme'</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>data 组件选项不再接受纯 js Object,而必须是 function.而组件和 mixins 或 extends 基类合并是,现在将浅层次合并.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Mixin = {</span><br><span class="line"> data() {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> user: {</span><br><span class="line"> name: <span class="string">'Jack'</span>,</span><br><span class="line"> id: <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> CompA = {</span><br><span class="line"> mixins: [Mixin],</span><br><span class="line"> data() {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> user: {</span><br><span class="line"> id: <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"> <span class="comment">// vue 2.0 合并后是</span></span><br><span class="line"> {</span><br><span class="line"> user: {</span><br><span class="line"> id: <span class="number">2</span>,</span><br><span class="line"> name: <span class="string">'Jack'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// Vue 3.0 合并后是</span></span><br><span class="line">{</span><br><span class="line"> user: {</span><br><span class="line"> id: <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>迁移建议: 对于依赖 mixin 的深度合并行为的用户,我们建议重构代码以完全避免这种依赖,因为 mixin 的深度合并非常隐式,这让代码逻辑更难理解和调试。</p><ol><li><p>过渡 class 名更改<br>过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from。.</p></li><li><p>移除功能</p></li></ol><ul><li>不再支持使用数字键吗作为 v-on 的修饰符</li><li><p>不再支持 config.keyCodes</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Vue.config.keyCodes = {</span><br><span class="line"> f1: <span class="number">112</span></span><br><span class="line">}</span><br><span class="line"><!-- 键码版本 --></span><br><span class="line"><input v-on:keyup<span class="number">.112</span>=<span class="string">"showHelpText"</span> /></span><br><span class="line"></span><br><span class="line"><!-- Vue <span class="number">3</span> 在 v-on 上使用 按键修饰符, 建议对任何要用作修饰符的键使用 kebab-cased (短横线) 大小写名称 --></span><br><span class="line"><input v-on:keyup.delete=<span class="string">"confirmDelete"</span> /></span><br></pre></td></tr></table></figure></li><li><p>$on,$off 和 $once 实例方法(全局事件侦听器)已被移除,应用实例不再实现事件触发接口。<br>$emit 仍然是现有 API 的一部分,因为它用于触发由父组件以声明方式附加的事件处理程序</p></li><li><p>Fileter 已删除,不再受支持,建议使用计算属性替代</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><p>{{ accountBalance | currencyUSD }}<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default{</span></span><br><span class="line"><span class="regexp">filters: {</span></span><br><span class="line"><span class="regexp"> currencyUSD(value) {</span></span><br><span class="line"><span class="regexp"> return '$' + value</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure></li><li><p>删除 inline-template 内联属性</p></li><li>删除$destroy 实例方法,用户不应再手动管理 Vue 组件的生命周期</li></ul><h3 id="vue3新特性"><a href="#vue3新特性" class="headerlink" title="vue3新特性"></a>vue3新特性</h3><ol><li><p>setup<br>setup是Vue3.0提供的一个新的属性,可以在setup中使用Composition API.setup函数有两个参数,分别是props和context。props 是组件外部传入进来的属性,contextcontext是一个对象,里面包含了三个属性attrs,slots,emit.<br>attrs 与 vue2.0 的 this.$attrs 一样,是外部传入未在 props 中定义的属性.<br>slots 对应 vue2.0 的 this.$slots 代表组件插槽.<br>emit 对应 vue2.0 的 this.$emit,对外暴露的事件.<br>setup 返回一个对象,对象中包含了组件使用到的 data 与一些函数或事件,也可以返回一个函数,对应 vue2.0 的 render 函数在里面可以使用 jsx.<br>不要在 setup 中使用 this,通过 props 和 content 基本可以满足开发需求.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> props: {</span><br><span class="line"> value: {</span><br><span class="line"> type: <span class="built_in">String</span>,</span><br><span class="line"> <span class="keyword">default</span>: <span class="string">""</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> setup(props) {</span><br><span class="line"> <span class="built_in">console</span>.log(props.value)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>composition API<br>在 vue2.0 中,我们在 data()函数中定义数据,在 methods,computed,watch 等等地方使用数据,书写逻辑.但随着功能增加,代码越来越难阅读和理解,因为现有的 api 迫使我们通过选项写代码,但有时候通过逻辑写代码更有意义.但 vue2.0 缺少一种简介的机制来提取和重用多个组件间的逻辑.</p></li></ol><p>了解 composition api 前,想了解下 reactive 和 ref.</p><h4 id="reactive"><a href="#reactive" class="headerlink" title="reactive"></a>reactive</h4><p>在 vue2.6 中有一个 新的 api, Vue.observer,通过这个 api 可以创建一个响应式对象.而 reactive 和 observer 功能基本一样.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">template</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span>{{ state.name }}<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> { reactive } <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"><span class="undefined"> setup() {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 通过reactive声明一个可响应式的对象</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> name: <span class="string">"test"</span></span></span><br><span class="line"><span class="undefined"> });</span></span><br><span class="line"><span class="javascript"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span></span><br><span class="line"><span class="javascript"> state.name = <span class="string">"test123"</span>;</span></span><br><span class="line"><span class="undefined"> }, 1000 * 5);</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="undefined"> state</span></span><br><span class="line"><span class="undefined"> };</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined">};</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>vue2.x 时,经常出现更改数据后页面没有刷新,需要使用 Vue.set()来解决.vue3.0 抛弃了 2.0 使用的 Object.defineProperty .使用 proxy 来监听.我们可以直接在reactive声明的对象上添加新的属性.<br>reactive 返回的不是原对象,而是 proxy 实例的一个全新对象.</p><h3 id="ref"><a href="#ref" class="headerlink" title="ref"></a>ref</h3><p>假如现在我们需要在一个函数里面声明用户的信息,那么我们可能会有两种不一样的写法<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 写法1</span></span><br><span class="line"><span class="keyword">let</span> name = <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">let</span> version = <span class="string">'3.0'</span></span><br><span class="line"><span class="comment">// 写法2</span></span><br><span class="line"><span class="keyword">let</span> info = {</span><br><span class="line"> name: <span class="string">'vue'</span>,</span><br><span class="line"> version: <span class="string">'3.0'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>对于写法1我们直接使用变量就可以了,而对于写法2,我们需要写成info.name的方式。我们可以发现info的写法与reactive是比较相似的,而Vue3.0也提供了另一种写法,就像写法1一样,即ref。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">template</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span>名称:{{ name }}<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> { ref } <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"><span class="undefined"> setup() {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> name = ref(<span class="string">"vue"</span>);</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'名称'</span>,name.value)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 5秒后修改name为 react</span></span></span><br><span class="line"><span class="javascript"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span></span><br><span class="line"><span class="javascript"> name.value = <span class="string">"react"</span>;</span></span><br><span class="line"><span class="undefined"> }, 1000 * 5);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="undefined"> name</span></span><br><span class="line"><span class="undefined"> };</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined">};</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure></p><p>reactive传入的是一个对象,返回的是一个响应式对象,而ref传入的是一个基本数据类型(其实引用类型也可以),返回的是传入值的响应式值.<br>reactive获取或修改属性可以直接通过state.xxx来操作,而ref返回值需要通过xxx.value的方式来修改或者读取数据。但是需要注意的是,在template中并不需要通过.value来获取值,这是因为template中已经做了解套。</p><ul><li>toRefs会把一个响应式对象的每个属性都转换为一个ref,在 setup 返回值中…roRefs(data),在模板中引用不需要再加上 data 前缀,可以直接使用变量.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { toRefs, reactive } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> setup() {</span><br><span class="line"> <span class="keyword">let</span> data = reactive({</span><br><span class="line"> count: <span class="number">0</span></span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> ...toRefs(data)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><ol><li>watch<br>vue2.0 中使用 watch 来监听结果的变化</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span>{</span><br><span class="line">watch:{</span><br><span class="line">name: {</span><br><span class="line">handler(newVal,oldVal){</span><br><span class="line">...</span><br><span class="line">},</span><br><span class="line">deep: <span class="literal">true</span>,</span><br><span class="line">immediate: <span class="literal">true</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 或使用</span></span><br><span class="line"><span class="keyword">this</span>.$watch(<span class="string">'name'</span>,() => {...},{<span class="attr">deep</span>:<span class="literal">true</span>})</span><br></pre></td></tr></table></figure><p>vue3.0 兼容 2.0 的写法,也提供了新的 api.分别是 watch 和 watchEffect.<br>watch 与2.0 $watch 用法一样.但可以监听单个值和函数的返回值,还可以监听多个数据源(放在一个数组中).<br>watchEffect会传入一个函数,然后立即执行这个函数,对函数中的响应式依赖进行监听,当依赖变化时,重新调用传入的函数.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { ref, watchEffect } <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> setup() {</span><br><span class="line"> <span class="keyword">const</span> id = ref(<span class="string">'0'</span>)</span><br><span class="line"> watchEffect(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="comment">// 先输出 0 然后两秒后输出 1</span></span><br><span class="line"> <span class="built_in">console</span>.log(id.value)</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> id.value = <span class="string">'1'</span></span><br><span class="line"> }, <span class="number">2000</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>vue2.0 中$watch 会返回一个函数用于停止监听.vue3.0 中 watch 和 watchEffect 也会返回一个用于 unwatch 的函数.</p><ol><li><p>computed<br>vue 3.0 中 computed 与 vue2.0 一样.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 2.0</span></span><br><span class="line">computed:{</span><br><span class="line">getName(){</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">this</span>.firstName + <span class="keyword">this</span>.lastName;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// vue3.0</span></span><br><span class="line"><span class="keyword">const</span> info = reactive({</span><br><span class="line">firstName: <span class="string">'xx'</span>,</span><br><span class="line">lastName: <span class="string">'xxx'</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">// getter</span></span><br><span class="line"><span class="keyword">const</span> getName = computed(<span class="function"><span class="params">()</span> =></span> info.firstName + info.lastName)</span><br><span class="line"><span class="comment">// 或 getter + setter</span></span><br><span class="line"><span class="keyword">const</span> getName = computed({</span><br><span class="line">get: <span class="function"><span class="params">()</span> =></span> info.firstName + info.lastName,</span><br><span class="line">set(val){</span><br><span class="line"><span class="keyword">const</span> name = val.split(<span class="string">'-'</span>)</span><br><span class="line">info.firstName = name[<span class="number">0</span>]</span><br><span class="line">info.lastName = name[<span class="number">1</span>]</span><br><span class="line">}</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>readonly<br>获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。只读代理是深层的:访问的任何嵌套 property 也是只读的。</p></li><li><p>provide 和 inject<br>provide 和 inject 启用依赖注入。只有在使用当前活动实例的 setup() 期间才能调用这两者。</p></li></ol>]]></content>
<summary type="html">
<h3 id="vue3-0-迁移指南"><a href="#vue3-0-迁移指南" class="headerlink" title="vue3.0 迁移指南"></a>vue3.0 迁移指南</h3><ol>
<li>全局 api 更改为 应用程序实例.<br>vue2.0
</summary>
<category term="前端 Vue" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF-Vue/"/>
<category term="Vue" scheme="http://yoursite.com/tags/Vue/"/>
</entry>
<entry>
<title>认识基金</title>
<link href="http://yoursite.com/2020/07/15/%E8%AE%A4%E8%AF%86%E5%9F%BA%E9%87%91/"/>
<id>http://yoursite.com/2020/07/15/认识基金/</id>
<published>2020-07-15T11:47:48.000Z</published>
<updated>2021-08-30T11:49:57.529Z</updated>
<content type="html"><![CDATA[<h1 id="认识基金"><a href="#认识基金" class="headerlink" title="认识基金"></a>认识基金</h1><h3 id="基金的种类"><a href="#基金的种类" class="headerlink" title="基金的种类"></a>基金的种类</h3><p>按照投资品种分类,可以分为:1.股票基金,2.债券基金,3.混合基金,4.货币基金</p><p>股票基金,他主要投资的品种是股票和债券,股票品种必须占总品种的80%以上,而债券则必须低于20%。<br>债券基金,一般投资的是股票和债券,他叫债券基金所以他投资品种的80%必须是债券,也就是说债券必须占大多数。货币型基金,一般对于投资的债券其实是没有要求的,他的投资产品一般为短期债券和银行收益比较稳定的债券等。我们常用的余额宝其实就是货币基金的一种。最后来说混合基金。一般混合型基金就是股票和债券的混合品种,由于是混合型,所以对股票和债券的占比并没有要求。基金经理可以随意自行买入不同比例的股票和债券。比如市场好的时候就买入多一点的股票来获取更多的利润,市场不好的时候就买入多一点的债券来分摊风险。当然啦,混合基金也分为偏股票型混合与偏债券型混合,按照名字来说他的股票和债券的占比也会有所不同。</p><p>按照交易渠道来分,又可以分为两种,分别是场内基金和场外基金。</p><p>场内基金说白了就是在证券公司内部才能购买的基金。购买这种基金一般来说需要去券商网点开通相应的证券账号。而场外基金呢,是由第三方的机构帮助你去购买基金。比如在支付宝或微信上都能购买基金,这就是所谓的场外基金。场外基金的好处是不用开户,也不用去下载app,并且还能给你自动设置投资的额度和周期。</p><p>按照运作方式来分可以分为开放式基金和封闭式基金。</p><p>我们市面上绝大部分基金都是开放式基金。所谓开放式基金,就会随拿随取的,一般来说没有任何的年限要求。而封闭式基金则有一定的年限要求</p><p>按照投资方式来分可以分为主动性基金和被动型基金两种。</p><p>主动基金一般由基金经理自行判断买入和卖出时间,还有自定决定买入哪些股票债券他们的持仓比例是多少,这都是由管理这只基金的基金经理来决定。因此这就很考验基金经理的投资和选择产品的水平了。<br>而被动型基金,也就是我们所天天念叨的指数型基金。他的买入卖出的时机和持仓的成分股等都跟基金经理的主观判断没关系,他只跟国家的经济和大盘的点数有关。</p><h3 id="辨识基金"><a href="#辨识基金" class="headerlink" title="辨识基金"></a>辨识基金</h3><p>公司名称 + 投资方向/基金特点 + 基金类型,基金的名字大多是由这3部分组成的。<br>国泰沪深 300 指数 A ,就是指 国泰基金管理有限公司 出品的 追踪“沪深 300”这个指数的 基金。<br>关于名字中的“优选”“灵活配置”“价值”“量化”,选基金的时候必须遵守一个原则,一切带有修饰词的名称都是纸老虎。</p><h4 id="基金名字中的ABC"><a href="#基金名字中的ABC" class="headerlink" title="基金名字中的ABC"></a>基金名字中的ABC</h4><p>货币基金 ABC 的区别在于申购门槛不同。A 的后缀的呢,一般申购门槛比较低,面向的是普通投资者. B 和 C的呢,一般申购门槛都比较高,主要面向的是资金量大的用户或者机构投资者。</p><p>债券基金的 ABC 主要在于收费方式的不同。债券基金中 A 类,一般是前端收取申购费,也就是在买的时候,不管你打算持有这只基金多长时间,都马上收取申购费。B 类呢,一般是后端收取申购费,就是买的时候先不扣你的申购费,等赎回的时候才一次性收取。那关于后缀为 C 的,一般会免去申购费,短期投资选 C,长期投资选A 或 B.</p><p>指数基金的 A和C,A 类不收销售服务费,但是会收申购费,赎回费,根据持有时长变化。C 类不收申购费,但要比 A 类多收一定的销售服务费,按日计提。持有时间大于7 天,赎回费率为 0。一般持有一年以内买 C,持有一年以上就买 A。</p><h4 id="场内基金和场外基金的具体区别"><a href="#场内基金和场外基金的具体区别" class="headerlink" title="场内基金和场外基金的具体区别"></a>场内基金和场外基金的具体区别</h4><p>1.费率的区别。场内交易费率更便宜一些。<br>2.基金购买或申购时价格的区别。场内基金购买的时候它的价格是实时波动的,就像股票一样。比如上午10点的时候它的价格是1块钱,这个时候你可以以1块钱的价格买入。到了下午2点,他的价格变成了2块钱的时候,你这时想买就必须花2块钱去买了。外基金他的价格是不受价格实时波动的。不管你如何波动,只要你是在下午三点收盘前申购,它都是以下午三点收盘的价格购入。<br>3.购买份数有区别。场内基金对购买份数有要求。跟股票一样,最少需要买1手,也就是100股。而场外就没有这个要求啦,最低好像是十块钱起投资,不管多少手都行,当然也没有多少手这个概念。<br>4.投资方式的区别。场外基金相比较场内基金在这块操作起来更便捷。比如,场外基金可以设置自动定投操作。<br>5.到账时间。场内基金买入后T+1日就可以卖出,而资金在成功交易后即可使用。场外基金一般申购后T+2日才可以赎回,资金到账时间一般为T+1到7个工作日。</p><h3 id="货币基金"><a href="#货币基金" class="headerlink" title="货币基金"></a>货币基金</h3><h4 id="货币基金的选择"><a href="#货币基金的选择" class="headerlink" title="货币基金的选择"></a>货币基金的选择</h4><p>1.成立时间;最好是在3-5年以上。首先他的历史收益没办法进行很有效的参考<br>2.基金的规模;规模适中的最好。最低不能低于20亿<br>3.流动性;一般来说有T+0和T+1两种交割方式。具体选择哪种看个人,我更倾向于选择T+0的方式。<br>4.收益率.正常的收益率情况,我们可以通过“万分收益率”和“7天年化收益率”来计算。万份收益了就是按照上一日或者上一个交易日的收益来算一万块本金能赚多少钱。而7天年化收益率则是,根据过去7天的收益总合计算出年化收益率。</p><h4 id="债券基金"><a href="#债券基金" class="headerlink" title="债券基金"></a>债券基金</h4><p>通常来说 1 年之内要用的钱,用货币基金打理是非常合适的,流动性好,安全性高,收益也远高于银行活期利息。<br>投资股票类基金,比如之后会讲到的指数基金,用的是 3 年以上用不到的闲钱。那么如果是 1-3 年用不到的钱怎么办?放货币基金有点太可惜啦,此时纯债基金就是非常好的选择,通常来说纯债基金可以有 6%-7%的年化收益率。还有一种情况,就是整个股票市场都涨疯了的牛市,此时低估的指数基金已经找不到了。但是我们的投资还是要继续呀,这时我们就可以卖出已经高估的指数基金,买入纯债基金。</p><h4 id="主动型基金的选取指标"><a href="#主动型基金的选取指标" class="headerlink" title="主动型基金的选取指标"></a>主动型基金的选取指标</h4><p>主动基金最大的好处其实也是他最大的毛病就是过于依赖了基金经理的个人能力。因为决定你购买的基金的成分股是哪些的,就是这个基金经理。如果这个基金经理个人能力牛逼,那么可能他选的成分股就厉害。就会使得这只基金涨的多,但是反之如果这只基金经理的能力一般般或者很水,那么他这一篮子股票尽选一些比较垃圾的股票作为成分股,那么可想而知你买的基金也够呛。这也说明一只基金经理的能力的重要性,他决定了这只基金业绩到底是牛逼还是垃圾。</p><p>到底该如何去挑选一只主动基金和如何去判断一只基金的基金经理到底如何。<br>第一,看收益率。一般来说看累积3年以上的收益率,然后选择收益率高的基金。<br>第二,看基金的成立时间。一般也是选成立3年以上的基金最好。<br>第三点,手续费。<br>第四点,基金经理的更换频率越低越好。<br>第五点,看基金公司的盈利能力。<br>最重要的是看该基金的持仓.</p><h3 id="指数基金"><a href="#指数基金" class="headerlink" title="指数基金"></a>指数基金</h3><p>在中国,主要的指数我们分为四大类型。上证50,中证500,沪深300和创业板指数。</p><p>上证50就是指在上海交易所,也称之为上交所上市的所有公司里面挑选市值最高的前50只股票,按照一定的权重比例和计算公式算出来的一个值。<br>而沪深300指的是在上海交易所和深圳交易所上市的所有公司中,挑选出市值和业绩最好的前300家公司,按照一定的权重比例和计算公式算出来的一个值。他反应的就是整个中国上市公司的一个总体表现。<br>接着中证500是由全部A股中剔除沪深300指数成份股及总市值排名前300名的股票后,总市值排名靠前的500只股票组成,综合反映中国A股市场中一批中小市值公司的股票价格表现。<br>创业板指数,就是以起始日为一个基准点,按照创业板所有股票的流通市值,一个一个计算当天的股价,再加权平均,与开板之日的“基准点”比较。</p><h4 id="ETF,ETF-联接,LOF"><a href="#ETF,ETF-联接,LOF" class="headerlink" title="ETF,ETF 联接,LOF"></a>ETF,ETF 联接,LOF</h4><p>ETF 基金。交易型开放式指数基金,通常又被称为交易所交易基金.特点一:跟踪指数的效果更好。特点二:相对场外的指数基金,购买成本更低。</p><p>ETF 联接。它其实就是为了方便我们普通投资者购买 ETF 基金而专门设计的。对于普通投资者而言,ETF 基金只能通过证券交易软件进行场内买卖,但又有 100份起售的限制。ETF 联接就是买 ETF 基金的基金,一般以不低于 90%的仓位投资于该标的 ETF 基金。</p><p>用一句话总结就是:ETF 是指数的跟屁虫,ETF 链接是 ETF 的跟屁虫。<br>LOF 基金是上市交易型开放式基金,大家看到名字里带 LOF 的指数型基金,就知道它既可以场内交易也可以场外交易了。</p>]]></content>
<summary type="html">
<h1 id="认识基金"><a href="#认识基金" class="headerlink" title="认识基金"></a>认识基金</h1><h3 id="基金的种类"><a href="#基金的种类" class="headerlink" title="基金的种类">
</summary>
<category term="基金" scheme="http://yoursite.com/categories/%E5%9F%BA%E9%87%91/"/>
<category term="基金" scheme="http://yoursite.com/tags/%E5%9F%BA%E9%87%91/"/>
</entry>
<entry>
<title>Truffle 框架开发区块链智能合约</title>
<link href="http://yoursite.com/2020/07/07/Truffle%20%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91%E5%8C%BA%E5%9D%97%E9%93%BE%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6/"/>
<id>http://yoursite.com/2020/07/07/Truffle 框架开发区块链智能合约/</id>
<published>2020-07-07T11:33:27.000Z</published>
<updated>2021-08-30T11:34:54.626Z</updated>
<content type="html"><![CDATA[<h1 id="Truffle-框架开发区块链智能合约"><a href="#Truffle-框架开发区块链智能合约" class="headerlink" title="Truffle 框架开发区块链智能合约"></a>Truffle 框架开发区块链智能合约</h1><p>Truffle是针对基于以太坊的Solidity语言的一套开发框架。本身基于Javascript。是基于 node.js 和 web3.js 的框架进行合约的编译,发布和调用.如果熟悉 node 开发,可以直接使用 web3.js 进行开发.如果擅长 java 语言,可以使用 web3j 开发.</p><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>开发及测试中需要安装Ethereum客户端,以支持JSON RPC API调用开发环境,推荐使用EthereumJS TestRPC。<br>如果使用 vscode 编辑器还可以安装 solidity 插件.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">npm install -g truffle</span><br><span class="line">npm: npm install Web3</span><br><span class="line">npm install -g ganache-cli <span class="comment"># ethereumJs TestRPC</span></span><br></pre></td></tr></table></figure></p><p>以太坊有很多客户端,基于 Go 语言开发的以太坊客户端 Geth 提供了 js 的运行环境可以基于 console 和 script.<br>而 EthereumJS TestRPC 是一个完整运行在内存中的区块链 可以再开发设备上适时返回,快速验证.等测试完成后在使用真实客户端发布.</p><h3 id="EthereumJS-TestRPC"><a href="#EthereumJS-TestRPC" class="headerlink" title="EthereumJS TestRPC"></a>EthereumJS TestRPC</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install -g ethereumjs-testrpc</span><br><span class="line">testrpc <span class="comment"># 启动</span></span><br></pre></td></tr></table></figure><h3 id="搭建私有链"><a href="#搭建私有链" class="headerlink" title="搭建私有链"></a>搭建私有链</h3><p>编写创世区块配置文件,然后执行初始化操作,完成后就可以启动私有链了.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">touch geth/gensis.json <span class="comment"># 配置文件</span></span><br><span class="line">mkdir db</span><br><span class="line">geth --datadir <span class="string">"./db"</span> init gensis.json <span class="comment"># 执行初始化命令</span></span><br><span class="line">geth --datadir <span class="string">"./db"</span> --rpc --rpcaddr=0.0.0.0 --rpcport 8545 <span class="comment"># 启动</span></span><br><span class="line">geth --datadir <span class="string">"./db"</span> attach <span class="comment"># 进入 js 控制台</span></span><br></pre></td></tr></table></figure></p><h3 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">truffle init</span><br></pre></td></tr></table></figure><p>初始化之后会出现几个目录:</p><p>test 用来测试应用和合约文件.</p><p>truffle.js 是 truffle 的配置文件.</p><h3 id="contract-意为合同"><a href="#contract-意为合同" class="headerlink" title="contract(意为合同)"></a>contract(意为合同)</h3><p>contract 是默认合约文件存放的地址.合约后缀是.sol表示 solidity,执行 <code>truffle compile --compile-all</code>编译合约.文件名和代码中的合约名要一致,区分大小写.通过 import 来什么依赖,会安装正确顺序来依次编译和关联库.编译输出在 build/contracts 文件中.</p><h3 id="migrations-意为迁移"><a href="#migrations-意为迁移" class="headerlink" title="migrations(意为迁移)"></a>migrations(意为迁移)</h3><p>migrations存放发布脚本的地址.移植是由一些 js 文件协助发布到以太坊网络.主要目的是用来缓存你的发布任务,当你的工程发生了一些重要改变,你将创建新的移植脚本来讲这些变化移植到区块链上.之前运行移植的记录历史会通过一个特殊的 migrations 来记录到链上.<br><code>truffle migrate</code>命令会很自信所有该目录下的移植脚本.文件名以数字开头描述结尾比如<code>1_initial_migration.js</code>,deployer 是部署器,你可以按照一定顺序发布任务,会按照从上到下依次执行.或使用 promise 来做出一个队列.,要实现不同条件的不同部署,可以给脚本添加第二个参数 network.<br>部署器有很多函数,deploy 函数来发布指定合约或合约数组,如果合约依赖某个库应该先部署这个依赖库.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ZhouCoin = artifacts.require(<span class="string">"ZhouCoin"</span>); <span class="comment">// 类似于 node 中的 node 中的 commonJS</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span>(<span class="params">deployer</span>)</span>{ </span><br><span class="line"> deployer.deploy(ZhouCoin)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>link 函数用来连接一个已经发布的库或合约.<br>then 函数是 promise 语法糖.<br>exec 函数来执行外部脚本.</p><h3 id="构建应用"><a href="#构建应用" class="headerlink" title="构建应用"></a>构建应用</h3><p>app 目录是文件运行默认目录.truffle 默认构建有一些特性,在浏览器内自动初始化应用包括引入编译合约,部署合约,配置以太坊客户端信息.<br>包含常见的依赖如 web3 和 ether Pudding,内置 es6 和 jsx, sass 支持, uglifyjs 支持.<br>app</p><pre><code>- javascripts / app.js- stylesheets / app.css- images- index.html</code></pre><p>然后在配置文件中配置<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="string">"build"</span>:{</span><br><span class="line"><span class="string">"index.html"</span>: <span class="string">"index.html"</span>,</span><br><span class="line"><span class="string">"app.js"</span>: [</span><br><span class="line"><span class="string">"javascripts/app.js"</span></span><br><span class="line">],</span><br><span class="line"><span class="string">"app.css"</span>: [</span><br><span class="line"><span class="string">"stylesheets/app.scss"</span></span><br><span class="line">],</span><br><span class="line"><span class="string">"images/"</span>: <span class="string">"images/"</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>然后<code>truffle build</code> 命令来创建前端工程.需要注意当前不支持 import 和 require,不能使用 webpack 和 commonJS 等工具来管理依赖.</p><h3 id="合约交互"><a href="#合约交互" class="headerlink" title="合约交互"></a>合约交互</h3><p>以太坊网络把数据的读写做了区分,写数据被称为 交易 Transaction, 读数据被称为 调用 Call.</p><p>交易:<br> 交易的接收地址如果是 合约地址会触发智能合约的函数运行,需要花费 gas.交易需要时间,函数执行后并不能立刻得到执行结果,大多数情况下很自信交易不会返回值,而是返回一个交易的 ID.</p><p>调用:<br> 调用可以再网络上执行代码,但不改变数据(也许仅仅是临时变量被更改).调用执行是免费的,典型行为就是读取数据,通过调用执行合约函数,你会立即得要结果.不花费 gas 也不改变网络化妆,立即执行且有返回结果.</p><p>接口(abstract)<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">"ConvertLib.sol"</span>;</span><br><span class="line"></span><br><span class="line">contract MetaCoin {</span><br><span class="line"> mapping (<span class="function"><span class="params">address</span> =></span> uint) balances;</span><br><span class="line"></span><br><span class="line"> event Transfer(address indexed _from, address indexed _to, uint256 _value);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">MetaCoin</span>(<span class="params"></span>) </span>{</span><br><span class="line"> balances[tx.origin] = <span class="number">10000</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">sendCoin</span>(<span class="params">address receiver, uint amount</span>) <span class="title">returns</span>(<span class="params">bool sufficient</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (balances[msg.sender] < amount) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> balances[msg.sender] -= amount;</span><br><span class="line"> balances[receiver] += amount;</span><br><span class="line"> Transfer(msg.sender, receiver, amount);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getBalanceInEth</span>(<span class="params">address addr</span>) <span class="title">returns</span>(<span class="params">uint</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> ConvertLib.convert(getBalance(addr),<span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getBalance</span>(<span class="params">address addr</span>) <span class="title">returns</span>(<span class="params">uint</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> balances[addr];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>合约有三个方法和一个构造方法.三个方法都可以执行交易和调用.其中只有 sendCoin 函数对网络造成了更改,所以它作为一个交易来执行.getBanance 函数是一个典型的调用函数,从网路中读取数据.<br>合约可以触发事件,事件与 web3 一样.合约接口都有一个 deployed()方法,表示部署到网络合约对应的抽象接口实例.或者通过 at(‘0x..’) 由地址得要接口实例.new()函数用来部署一个全新的合约到网络中,这是一个交易会改变网络状态.</p><h3 id="测试合约"><a href="#测试合约" class="headerlink" title="测试合约"></a>测试合约</h3><p>truffle 使用 mocha 测试框架来做自动化测试,使用 Chai 来做断言.Truffle 只会运行js,es,es6,jsx 结尾的测试文件.<br>命令<code>truffle test xx.js</code>来测试某文件.</p><h3 id="控制台"><a href="#控制台" class="headerlink" title="控制台"></a>控制台</h3><p>在测试时与合约交互是很频繁的,Truffle 提供了一个交互式控制台可以用更简单的方式来和合约交互.<code>truffle console</code>来启动控制台,会自动连接到一个运行中的以太坊客户端,控制台支持 truffle 命令,<code>migrate --reset</code> 与外部执行<code>truffle migrate --reset</code>效果是一样的.</p><h3 id="外部脚本"><a href="#外部脚本" class="headerlink" title="外部脚本"></a>外部脚本</h3><p>你也行会经常使用外部脚本与你的合约进行交互,Truffle 提供了一个简单的方式进行这个<code>truffle exec xx.js</code>,为了外部脚本执行正常,truffle 需要他们能通过 js 的模块方式导出一个函数,且有一个回调函数作为参数.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span>(<span class="params">callback</span>)</span>{</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="工作流"><a href="#工作流" class="headerlink" title="工作流"></a>工作流</h3><p>truffle 提供<code>truffle watch</code>和<code>truffle serve</code>命令,一个是用作修改后重构合约,一个用做修改后重编译部署构建.</p><h3 id="truffle-js-配置文件"><a href="#truffle-js-配置文件" class="headerlink" title="truffle.js 配置文件"></a>truffle.js 配置文件</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line">build: {}, <span class="comment">// 构建</span></span><br><span class="line">network: {</span><br><span class="line">development:{</span><br><span class="line"><span class="comment">// host,port,network_id: "*"</span></span><br><span class="line">},</span><br><span class="line">staging:{</span><br><span class="line"><span class="comment">// host,port,network_id: "*"</span></span><br><span class="line">},</span><br><span class="line">ropsten: {</span><br><span class="line"><span class="comment">// host,port,network_id: "*"</span></span><br><span class="line">}</span><br><span class="line">}, <span class="comment">// 指定移植时使用哪个网络</span></span><br><span class="line">mocha: {} <span class="comment">// 测试框架的配置选项</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="DAPP前端交互"><a href="#DAPP前端交互" class="headerlink" title="DAPP前端交互"></a>DAPP前端交互</h3><p>为了在前端 js 代码中能后使用 Truffle 提供的合约抽象,我们需要 truffle-default-builder,它可以帮助我们在把合约抽象整合到 js 中.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save [email protected]</span><br></pre></td></tr></table></figure></p><p>然后在 truffle.js 中增加配置 network.development 配置.然后执行 truffle build 命令</p>]]></content>
<summary type="html">
<h1 id="Truffle-框架开发区块链智能合约"><a href="#Truffle-框架开发区块链智能合约" class="headerlink" title="Truffle 框架开发区块链智能合约"></a>Truffle 框架开发区块链智能合约</h1><p>Tr
</summary>
<category term="Truffle DAPP" scheme="http://yoursite.com/categories/Truffle-DAPP/"/>
<category term="Truffle DAPP" scheme="http://yoursite.com/tags/Truffle-DAPP/"/>
</entry>
<entry>
<title>搭建 MOCK 服务</title>
<link href="http://yoursite.com/2020/06/30/%E6%90%AD%E5%BB%BA%20MOCK%20%E6%9C%8D%E5%8A%A1/"/>
<id>http://yoursite.com/2020/06/30/搭建 MOCK 服务/</id>
<published>2020-06-30T09:57:43.000Z</published>
<updated>2021-08-30T09:59:14.927Z</updated>
<content type="html"><![CDATA[<h1 id="搭建-MOCK-服务"><a href="#搭建-MOCK-服务" class="headerlink" title="搭建 MOCK 服务"></a>搭建 MOCK 服务</h1><h3 id="安装-mock-js"><a href="#安装-mock-js" class="headerlink" title="安装 mock.js"></a>安装 mock.js</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm run mockjs</span><br><span class="line">npm install supervisor -g</span><br></pre></td></tr></table></figure><p>supervisor是一个用来运行node程序监控程序。(类似于 pm2)它是Nodejs的一个很小的监控脚本。它运行在你的程序中,并且监控你的代码变化,所以你可以进行代码热更新,而不用担心内存泄漏和确保你清理所有模块间的引用。</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">"scripts":{</span><br><span class="line">"mock": "supervisor -w mock ./xxx/http.js",</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h3><p>Mock.mock(rurl,rtype,data)</p><ul><li>rurl: 表示需要拦截的 URL,可以是 URL 字符串或 URL 正则。例如 /\/domain\/list.json/、’/domian/list.json’</li><li>rtype: 可选.表示需要拦截的 Ajax 请求类型。例如 GET、POST、PUT、DELETE 等。</li><li>data: 对象,字符串, 函数.</li></ul><p>Mock.setup( settings )<br>配置拦截 Ajax 请求时的行为。支持的配置项有:timeout。Mock.setup({<br> timeout: ‘200-600’<br>})</p><p>Mock.valid(template, data)校验真实数据 data 是否与数据模板 template 匹配。</p><p>Mock.toJSONSchema(template)把 Mock.js 风格的数据模板 template 转换成 JSON Schema。</p><p>Mock.Random是一个工具类,用于生成各种随机数据。<br>Mock.Random 的方法在数据模板中称为『占位符』,书写格式为 @占位符(参数 [, 参数]) 。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Random = Mock.Random</span><br><span class="line">Random.email()</span><br><span class="line"><span class="comment">// => "[email protected]"</span></span><br><span class="line">Mock.mock(<span class="string">'@email'</span>)</span><br><span class="line"><span class="comment">// => "[email protected]"</span></span><br><span class="line">Mock.mock( { <span class="attr">email</span>: <span class="string">'@email'</span> } )</span><br><span class="line"><span class="comment">// => { email: "[email protected]" }</span></span><br></pre></td></tr></table></figure></p><p>提供的占位符有以下类型:<br>|type|method|<br>|Basic|boolean,interger,float,string,date,time,now…|<br>|Image|image,dataImage|<br>|Color|color|<br>|Text|parafraph,sentence,word,title,cparafraph…|<br>|Color|color|<br>|Name|first,last,name,cfirst,clast,cname|<br>|Web|url,email,ip,tld,domain|<br>|Address|area,region|<br>|Other|id,picl…|</p><h3 id="使用-node-的http模块"><a href="#使用-node-的http模块" class="headerlink" title="使用 node 的http模块"></a>使用 node 的http模块</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>)</span><br><span class="line"><span class="keyword">const</span> Mock = <span class="built_in">require</span>(<span class="string">'mockjs'</span>)</span><br><span class="line"></span><br><span class="line">http</span><br><span class="line"> .createServer(<span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> res.writeHead(<span class="number">200</span>, {</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/json;charset=utf-8'</span>,</span><br><span class="line"> <span class="string">'Access-Control-Allow-Origin'</span>: req.headers.origin || <span class="string">'*'</span>,</span><br><span class="line"> <span class="string">'Access-Control-Allow-Methods'</span>: <span class="string">'*'</span>,</span><br><span class="line"> <span class="string">'Access-Control-Allow-Headers'</span>: <span class="string">'*'</span>,</span><br><span class="line"> <span class="string">'Access-Control-Allow-Credentials'</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="string">'Cache-Control'</span>: <span class="string">'no-cache,no-store'</span>, <span class="comment">// clear cache</span></span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">if</span> (req.method === <span class="string">'OPTIONS'</span>) {</span><br><span class="line"> res.end(<span class="literal">null</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (req.method === <span class="string">'POST'</span>) {</span><br><span class="line"> <span class="keyword">let</span> postData = <span class="string">''</span></span><br><span class="line"> req.addListener(<span class="string">'data'</span>, (dataBuffer) => (postData += dataBuffer))</span><br><span class="line"> req.addListener(<span class="string">'end'</span>, () => {</span><br><span class="line"> postData = <span class="built_in">JSON</span>.parse(postData)</span><br><span class="line"> <span class="keyword">const</span> data = []</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> res.end(<span class="built_in">JSON</span>.stringify(data))</span><br><span class="line"> }, <span class="built_in">parseInt</span>((<span class="built_in">Math</span>.random() - <span class="number">0.5</span> + <span class="number">1</span>) * <span class="number">500</span>, <span class="number">10</span>)) <span class="comment">// 随机数</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (req.method === <span class="string">'GET'</span>) {</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> res.end(Mock.mock(<span class="string">'@cname'</span>))</span><br><span class="line"> }, <span class="built_in">parseInt</span>((<span class="built_in">Math</span>.random() - <span class="number">0.5</span> + <span class="number">1</span>) * <span class="number">500</span>, <span class="number">10</span>)) <span class="comment">// 随机数</span></span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> .listen(<span class="number">1111</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'listening port 1111'</span>)</span><br></pre></td></tr></table></figure><h3 id="使用concurrently同时开启前端服务和后端服务"><a href="#使用concurrently同时开启前端服务和后端服务" class="headerlink" title="使用concurrently同时开启前端服务和后端服务"></a>使用concurrently同时开启前端服务和后端服务</h3><ul><li>安装<code>npm install concurrently --save-dev</code></li><li>更改 package.json 的 script `”xx”: “concurrently \”npm run xxx\” \”npm run xxx2\”” </li></ul>]]></content>
<summary type="html">
<h1 id="搭建-MOCK-服务"><a href="#搭建-MOCK-服务" class="headerlink" title="搭建 MOCK 服务"></a>搭建 MOCK 服务</h1><h3 id="安装-mock-js"><a href="#安装-mock-js"
</summary>
<category term="前端 Mock" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF-Mock/"/>
<category term="Mock" scheme="http://yoursite.com/tags/Mock/"/>
</entry>
<entry>
<title>JS 函数式编程指南</title>
<link href="http://yoursite.com/2020/06/16/JS%20%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B%E6%8C%87%E5%8D%97/"/>
<id>http://yoursite.com/2020/06/16/JS 函数式编程指南/</id>
<published>2020-06-16T05:19:21.000Z</published>
<updated>2021-08-30T09:51:01.505Z</updated>
<content type="html"><![CDATA[<h1 id="JavaScript-函数式编程"><a href="#JavaScript-函数式编程" class="headerlink" title="JavaScript 函数式编程"></a>JavaScript 函数式编程</h1><h3 id="第一章-走进函数式"><a href="#第一章-走进函数式" class="headerlink" title="第一章 走进函数式"></a>第一章 走进函数式</h3><ol><li>例子<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">document</span>.querySelector(<span class="string">"#msg"</span>).innerHTML = <span class="string">'<h1>hello world</h1>'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用函数封装这段代码</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">printMessage</span>(<span class="params">elementId,format,message</span>)</span>{</span><br><span class="line"><span class="built_in">document</span>.querySelector(<span class="string">`#<span class="subst">${elementId}</span>`</span>).innerHTML = <span class="string">`<<span class="subst">${format}</span>><span class="subst">${message}</span></<span class="subst">${format}</span>>`</span>;</span><br><span class="line">}</span><br><span class="line">printMessage(<span class="string">'msg'</span>,<span class="string">'h1'</span>,<span class="string">'hello world'</span>);</span><br><span class="line"><span class="comment">// 以上仍然不是一段可复用的代码,用函数式编程如下</span></span><br><span class="line"><span class="keyword">var</span> printMessage = run(addToDom(<span class="string">'msg'</span>),h1,echo);</span><br><span class="line">printMessage(<span class="string">'hello world'</span>);</span><br><span class="line"><span class="comment">// 将程序分解为多个函数,再将他们组合起来完成一系列的操作.</span></span><br><span class="line"><span class="comment">// 当需求更改为在控制台打印 3 遍文本信息,就可以改为一下代码</span></span><br><span class="line"><span class="keyword">var</span> printMessage = run(<span class="built_in">console</span>.log,repeat(<span class="number">3</span>),echo);</span><br><span class="line">printMessage(<span class="string">'hello world'</span>);</span><br></pre></td></tr></table></figure></li></ol><p>函数式编程的特征: 声明式编程,纯函数,引用透明,不可变性.</p><ol><li>声明式编程<br>目前更主流的是命令式编程和面向对象编程.我们来看一个命令式的例子.假设你需要计算一个数组中所有数的平方.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < array.length; i++) {</span><br><span class="line">array[i] = <span class="built_in">Math</span>.pow(array[i],<span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line">array; <span class="comment">// [0,1,4,9,16]</span></span><br></pre></td></tr></table></figure></li></ol><p>命令式编程是很具体的告诉计算机如何执行某个任务.而声明式编程是将程序的描述和求值分离开来.它关注与如何使用各种表达式来描述程序逻辑.你可以在 SQL 语句中找到声明式编程的例子.<br>可以将 es6 的 lambda 表达式和箭头函数将循环抽象成函数,减少代码的书写.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>].map(<span class="function"><span class="params">num</span> =></span> <span class="built_in">Math</span>.pow(num,<span class="number">2</span>)); <span class="comment">// [0,1,4,9,16]</span></span><br></pre></td></tr></table></figure></p><p>为什么要去掉代码循环?因为循环是命令控制结构,很难重用,并且很难插入其他操作中.并且要尽量做到无副作用无状态变化,既纯函数.</p><ol><li>副作用带来的问题和纯函数<br>函数式编程基于一个前提,既使用纯函数构建具有不变形的程序.考虑以下函数<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> counter = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">increment</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">return</span> ++counter;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>以上函数不是一个纯函数,它在读取外部资源时会产生副作用.还有一个例子 Date.now,它的输出是不可预见和不一致的.<br>另一个副作用是通过 this 关键字访问实例数据时,由于 js 语言的特性,它决定了一个函数在运行时的上下文,这往往导致很难去推理代码.<br>以下行为都可能导致副作用:</p><pre><code>- 改变一个全局的变量,属性,或数据结构- 改变一个函数参数的原始值- 处理用户输入- 抛出异常又被当前函数捕获- 屏幕打印或记录日志- 查询 html 文档,浏览器数据或访问数据库</code></pre><p>以下案例是一个命令式程序,它听过 SSN 号码找到一个学生的记录渲染到页面上.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">showStudent</span>(<span class="params">ssn</span>)</span>{</span><br><span class="line"><span class="keyword">var</span> student = db.get(ssn);</span><br><span class="line"><span class="keyword">if</span>(student != <span class="literal">null</span>){</span><br><span class="line"><span class="built_in">document</span>.querySelector(<span class="string">`#<span class="subst">${elementId}</span>`</span>).innerHTML = <span class="string">`<span class="subst">${student.ssn}</span> - <span class="subst">${student.name}</span>`</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> Erroe(<span class="string">'student not found!'</span>)</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">showStudent(<span class="string">'444-44-4444'</span>);</span><br></pre></td></tr></table></figure></p><p>该函数副作用有: 访问外部数据 db,全局变量 elementId 可能随时会改变,直接修改可外部共享的全局资源 html,抛出的异常会导致整个程序栈回退并结束.<br>如何使用函数式编程应对这种情况呢? 首先将长函数分离成多个且单一的短函数,其次通过显式的将外部依赖都定义为函数参数来减少副作用.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> find = curry(<span class="function"><span class="keyword">function</span>(<span class="params">db,id</span>)</span>{</span><br><span class="line"><span class="keyword">var</span> obj = db.get(id);</span><br><span class="line"><span class="keyword">if</span>(obj === <span class="literal">null</span>){</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'object is not found'</span>)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> obj;</span><br><span class="line">})</span><br><span class="line"><span class="keyword">var</span> csv = (student){</span><br><span class="line"><span class="keyword">return</span> <span class="string">`<span class="subst">${student.ssn}</span> - <span class="subst">${student.name}</span>`</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> append = curry(<span class="function"><span class="keyword">function</span>(<span class="params">elementId,info</span>)</span>{</span><br><span class="line"><span class="built_in">document</span>.querySelector(elementId).innerHTML = info;</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 调用以上函数</span></span><br><span class="line"><span class="keyword">var</span> student = run(append(<span class="string">'#student'</span>),csv,find(db));</span><br><span class="line">student(<span class="string">'444-44-4444'</span>);</span><br></pre></td></tr></table></figure><p>这个程序仍然有些问题,find 函数有一个检查 null 的分支.当一个函数能够确保有相同的返回值,它使得函数的结果一致且可预测,这就是纯函数的一个特质,引用透明.</p><ol><li>引用透明和可置换性<br>如果一个函数对于相同的输入始终产生相同的结果,它就是引用透明的.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> increment = <span class="function"><span class="params">counter</span> =></span> counter + <span class="number">1</span>;</span><br></pre></td></tr></table></figure></li></ol><p>它不仅能使代码易于测试,还更容易推理整个程序.</p><ol><li>储存不可变数据<br>不可变数据指那些创建后不能更改的数据,js 的所有基本类型本质上不可变,但数组或对象都是可变的.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sortDesc = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> arr.sort(<span class="function"><span class="keyword">function</span>(<span class="params">a,b</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> b-a;</span><br><span class="line">})</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line">sortDesc(arr);<span class="comment">// [4,3,2,1]</span></span><br></pre></td></tr></table></figure></li></ol><p>乍一看这段代码正常,但是 array.sort 函数是有状态的,会导致排序过程中产生副作用,因为原始的引用被修改了.</p><ol><li>总结<br>函数式编程是指为创建不可变程序,通过消除外部可见的副作用,来对纯函数的声明式求值过程.</li></ol><h3 id="第二章-高阶-JavaScript"><a href="#第二章-高阶-JavaScript" class="headerlink" title="第二章 高阶 JavaScript"></a>第二章 高阶 JavaScript</h3><ol><li>一等函数<br>函数是函数式编程的工作单元和中心,函数只有在返回一个有价值的结果(而不是 null和 undefined)时才有意义.同时,我们需要区分表达式(返回一个值的函数)与语句(不返回值得函数).函数式编程完全依赖表达式,无值函数在函数式编程下没有意义.<br>在 js 中,任何函数都是 Function 类型的一个实例,函数 length 属性可以获取形参的长度,apply 和 call 可以调用函数并加入上下文,不同的是 apply 函数接收一个参数数组,而 call 接收一系列参数.但函数式编程不建议这样做,因为它永远不会依赖于函数的上下文状态.</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建一个函数,接受一个函数参数,并返回取反其结果的函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">negate</span>(<span class="params">func</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">return</span> !(func.apply(<span class="literal">null</span>,<span class="built_in">arguments</span>));</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNull</span>(<span class="params">val</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> val === <span class="literal">null</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> isNotNull = negate(isNull);</span><br><span class="line">isNotNull(<span class="literal">null</span>); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><ol><li>闭包和作用域<br>在 js 之前闭包只存在于函数式编程语言中,js 是第一个在主流开发中应用闭包的语言.<br>闭包是一种能后在函数声明过程中将环境信息和所属函数绑定在一起的数据结构.从本质上讲,闭包就是函数继承而来的作用域.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">makeAddFunction</span>(<span class="params">amount</span>)</span>{</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">number</span>)</span>{</span><br><span class="line"><span class="comment">// add 函数可以通过词法作用域访问 amount</span></span><br><span class="line"><span class="keyword">return</span> number + amount;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> add;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> addTenTo = makeAddFunction(<span class="number">10</span>);</span><br><span class="line">addTenTo(<span class="number">1</span>); <span class="comment">// 11</span></span><br></pre></td></tr></table></figure></li></ol><p>闭包会在其声明时记住其作用域内的所有变量,并防止他们被垃圾回收机制回收.<br>js 的作用域分为全局作用域,函数作用域,伪块作用域.</p><pre><code>- 全局作用域,window 或 global,会有副作用,尽量避免使用- 函数作用域,可以嵌套,由内而外向上查找,直到全局作用域,推荐使用.- 伪块作用域,如 for,while,id,switch 语句,with,try..catch..等.无法从块外部访问</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dowork</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(!myVar){</span><br><span class="line"><span class="keyword">var</span> myVar = <span class="number">10</span>;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(myVar); <span class="comment">// 10</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>js 有一个内部机制,将所有变量和函数提取至作用域的顶部.<br>es6 提供了 let,const 等关键字定义的变量不会进行提升.</p><ol><li>闭包的实际应用</li></ol><p><strong>模拟私有变量</strong><br>js 并没有一个 private 修饰符来限定对象中私有变量和函数的访问,我们可以使用闭包来完成.<br>闭包还可以管理全局的命名空间,既模块模式,它采用立即执行函数表达式IIFE,在 封装内部变量的同时,有效减少了全局引用.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 一个模块框架的示例</span></span><br><span class="line"><span class="keyword">var</span> myModule = (<span class="function"><span class="keyword">function</span> <span class="title">myModule</span>(<span class="params">export</span>)</span>{</span><br><span class="line"><span class="comment">// 给 IIFE 一个名字方便栈追踪</span></span><br><span class="line"><span class="keyword">let</span> _myprivateVar = ...;<span class="comment">//无法从外部访问这个变量,但对内的方法可以访问.</span></span><br><span class="line"><span class="keyword">export</span>.method1 = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line">...</span><br><span class="line">}</span><br><span class="line"><span class="keyword">export</span>.method2 = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line">...</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">export</span>;</span><br><span class="line">}(myModule || {}));</span><br></pre></td></tr></table></figure></p><p>对象 myModule 在全局作用域创建,之后传递给一个 IIFE 函数表达式并立即执行.由于 js 的函数作用域,变量_myprivateVar 和其他变量都是函数的局部变量,闭包使得返回的对象能够安全的访问模块中的所有内部属性.</p><p><strong>异步服务端调用</strong></p><p>js 中的函数可以作为回调函数传递给其他函数,假设需要对服务器发起一次请求,并在响应时得到通知,常用的方式就是提供一个回调函数.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">getJson(<span class="string">'/student'</span>,(student) =>{</span><br><span class="line">getJson(<span class="string">'/students/grades'</span>,</span><br><span class="line">grades => processGrades(grades),</span><br><span class="line">error => <span class="built_in">console</span>.log(error)),</span><br><span class="line">error => <span class="built_in">console</span>.log(error)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>getJson 是一个高阶函数,它接收两个回调作为参数,一个处理成功的函数,一个处理失败的函数.如果需要多次请求很容易进入回调地狱.</p><h3 id="第三章-轻数据结构-重操作"><a href="#第三章-轻数据结构-重操作" class="headerlink" title="第三章 轻数据结构,重操作"></a>第三章 轻数据结构,重操作</h3><ol><li><p>理解程序的控制流<br>程序为实现业务目标进行的路径就是控制流.命令式程序需要通过暴露所有的必要步骤才能详细的描述其控制流,这里面通常涉及大量的循环和分支以及各种变量.然而函数式程序多使用简单拓扑链接的黑盒操作组合成较小的程序化控制流,这些链接在一起的操作只是一些能够将状态传递给下一个操作的高阶函数.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">opta().optb().optc()... <span class="comment">// 链式结构</span></span><br></pre></td></tr></table></figure></li><li><p>链接方法</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'function programing'</span>.substring(<span class="number">0</span>,<span class="number">10</span>).toLowerCase() + <span class="string">'is fun'</span>;</span><br></pre></td></tr></table></figure></li></ol><p>通过一系列变换后的结果与原字符串毫无引用关系,无副作用.如果用更加函数式的写法如下:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">concat(toLowerCase(subString(<span class="string">'function programing'</span>,<span class="number">1</span>,<span class="number">10</span>)),<span class="string">'is fun'</span>);</span><br></pre></td></tr></table></figure></p><p>这样虽然跟复合函数式的定义,但是较难阅读,需要一层层剥离外部函数,就行剥离洋葱一样.</p><ol><li>函数链<br>面向对象将继承作为代码重用的主要手段,比如在 java 中有继承与基础接口 List 的 ArrayList,LinkedLise 等.<br>但在函数式编程中是使用如数组这样的普通类型并施加在一套高阶操作上,通常接收函数作为参数,减少副作用等等.</li></ol><p>lambda 表达式,也被称为箭头函数.源自函数式编程,可以用较简介的语法声明一个匿名函数.它总是返回一个值.且能够与 map,reduce 等高阶函数配合使用.我们在接下来用 lodash 函数库来演示,它为了能够替换 underscore 采用了和它一样的 API.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用 map 做数据变换</span></span><br><span class="line">_.map([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>],v=> <span class="number">2</span>*v); <span class="comment">//2,4,6</span></span><br></pre></td></tr></table></figure><p>我们不需要再写循环的代码,也不用处理奇怪的作用域问题了.由于其不可变,因此输出一个全新的数组.<br>函数式库可以辅助我们开发,写出纯函数式的代码.<br>map是一个只会从左到右遍历的操作,对应重右到左遍历必须反转数组,但 js 中的 Array.reverse() 会改变原数组,所以我们可以配合 lodash 中的 reverse 配合 map 进行操作.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]).reverse().map(<span class="function"><span class="params">v</span> =></span> <span class="number">2</span>*v); <span class="comment">// 6,4,2</span></span><br></pre></td></tr></table></figure></p><p>高阶函数 reduce 将一个数组中的元素精简为一个值,该值是每个元素累计而得.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]).reduce( <span class="function">(<span class="params">memo,v</span>) =></span> memo+v,<span class="number">0</span> )</span><br></pre></td></tr></table></figure></p><p>除此之外,lodash 还提供了 every,some,filter 等辅助函数.</p><ol><li>代码推理<br>函数式编程中每个函数只完成一部分功能,但组合在一起就可以解决很多问题,下面介绍一种能够连接一组函数来构建程序的方法(声明式惰性计算函数链).<br>假设需要对一组姓名进行读取,去重,排序等操作,命令式代码如下:<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> names =[<span class="string">'alozno church'</span>,<span class="string">'Jaskell cjrl'</span>,<span class="string">'Terjdf'</span>,<span class="string">'asdfgg'</span>]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleName</span>(<span class="params">names</span>)</span>{</span><br><span class="line"><span class="keyword">var</span> result = []</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>;i<names.length;i++){ <span class="comment">// 遍历数组</span></span><br><span class="line"><span class="keyword">var</span> n = names[i]</span><br><span class="line"><span class="keyword">if</span>(n !== <span class="literal">null</span> && n !== <span class="literal">undefined</span>){ <span class="comment">// 检查是否合法</span></span><br><span class="line"><span class="keyword">var</span> ns = n.replace(<span class="regexp">/_/</span>,<span class="string">' '</span>).split(<span class="string">' '</span>) <span class="comment">// 规范数据</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> j=<span class="number">0</span>;j< ns.length;j++){</span><br><span class="line"><span class="keyword">var</span> p = ns[j] <span class="comment">// 处理数据</span></span><br><span class="line">p = p.charAt(<span class="number">0</span>).toUpperCase() + p.slice(<span class="number">1</span>);</span><br><span class="line">ns[j] = p;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (result.indexOf(ns.join(<span class="string">' '</span>))< <span class="number">0</span>) { <span class="comment">// 去除重复元素</span></span><br><span class="line">result.push(ns.join(<span class="string">' '</span>)) </span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">result.sort(); <span class="comment">// 数组排序</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>用函数式代码实现如下:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">_.chain(names) <span class="comment">// 初始化函数链</span></span><br><span class="line">.filter(isValid) <span class="comment">// 去除非法值</span></span><br><span class="line">.map(<span class="function"><span class="params">s</span> =></span> s.replace(<span class="regexp">/_/</span>,<span class="string">' '</span>)) <span class="comment">// 规范数据</span></span><br><span class="line">.uniq() <span class="comment">// 去重</span></span><br><span class="line">.map(_.startCase) <span class="comment">// 大写首字母</span></span><br><span class="line">.sort() <span class="comment">// 排序</span></span><br><span class="line">.value(); <span class="comment">// 返回封装对象的最终值</span></span><br></pre></td></tr></table></figure></p><p>对一个对象使用 chain 方法会封装这个对象,并之后的每次方法调用都返回这个封装的对象,当完成计算使用 value()函数取得最终值.<strong>使用 chain 链式调用的好处是可以创建具有惰性计算能力的复杂程序,在调用 value()之前并不会真正的执行任何操作.</strong> 链中的每个函数都以一种不可变的方式来处理换上一个函数构建的新数组.这有助于过渡到 point-free 编程风格的理解.</p><p>类 SQL 的数据:函数即数据.<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> p.firstname,p.birthYear <span class="keyword">from</span> person <span class="keyword">where</span> p.birthYear > <span class="number">1903</span> <span class="keyword">and</span> p.country <span class="keyword">IS</span> <span class="keyword">Not</span> <span class="string">'US'</span></span><br><span class="line"><span class="keyword">Group</span> <span class="keyword">By</span> p.firstname,p.birthYear</span><br></pre></td></tr></table></figure></p><p>lodash 支持一种称为 mixins 的函数,可以为核心库拓展新的函数.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">_.mixin({</span><br><span class="line"><span class="string">'select'</span> : _.pluck,</span><br><span class="line"><span class="string">'from'</span>: _.chain,</span><br><span class="line"><span class="string">'where'</span>: _.filter,</span><br><span class="line"><span class="string">'groupBy'</span>: _.sortByOrder</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>应用此 mixin 对象后就可以编写类 sql 的程序<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">_.from(persons)</span><br><span class="line">.where(<span class="function"><span class="params">p</span> =></span> p.birthYear > <span class="number">1903</span> && p.country !== <span class="string">'US'</span>)</span><br><span class="line">.groupBy([<span class="string">'firstname'</span>,<span class="string">'birthYear'</span>])</span><br><span class="line">.select(<span class="string">'firstname'</span>,<span class="string">'birthYear'</span>)</span><br><span class="line">.value();</span><br></pre></td></tr></table></figure></p><ol><li>递归<br>递归是一种通过将问题分解为较小的自相似问题来解决问题本身的技术,递归函数主要包含两方面,一是终止条件,二是递归条件.<br>来解决一个简单的问题,对数组中所有的值进行求和.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//老规矩,先命令式,再函数式.</span></span><br><span class="line"><span class="keyword">var</span> acc = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>;i<nums.length;i++){</span><br><span class="line">acc += nums[i]</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 函数式</span></span><br><span class="line">_(nums).reduce(<span class="function">(<span class="params">acc,current</span>) =></span> acc + current, <span class="number">0</span>);</span><br></pre></td></tr></table></figure></li></ol><p>递归和迭代是一个硬币的两面,在不可变条件下递归提供了一种更强大的迭代替代方法.纯函数式语言甚至没有标准的循环结构,如 for,while 等,因为所有循环都是递归完成的.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 递归求和</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">arr</span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(_.isEmpty(arr)){ <span class="comment">// 终止条件</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> _.first(arr) + sum(_.rest(arr)); <span class="comment">// 递归条件</span></span><br><span class="line">}</span><br><span class="line">sum([]); <span class="comment">// 0</span></span><br><span class="line">sum([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>])<span class="comment">// 6</span></span><br></pre></td></tr></table></figure></p><p>从底层看,递归调用会在栈中不断堆叠,但算法满足终止条件时,运行时会展开调用栈并执行加操作,因此所有返回语句都将被执行,递归就是通过这种机制代替循环.但是注意编译器在处理循环的优化问题是很强大的,比如 es6 带来了<strong>尾调用优化</strong>,可以使递归和迭代的性能更加接近.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span> (<span class="params">arr,acc=<span class="number">0</span></span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(_.isEmpty(arr)){ <span class="comment">// 终止条件</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> sum(_.rest(arr),acc+_.first(arr));<span class="comment">//发生在尾部的递归调用</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p> 我们之前已经利用函数式技术解析过一些扁平化数据,比如数组.但这些操作对树形数据结构是无效的.<br> 因为 js 没有内置的树形对象,所以需要基于节点,创建一种简单的数据结构.节点包括当前值,父节点引用,以及子节点数组的对象.<br> 树是包含了一个根节点的递归定义的数据结构.<br> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Tree</span></span>{</span><br><span class="line"><span class="keyword">constructor</span>(root){</span><br><span class="line"><span class="keyword">this</span>._root = root;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">static</span> map(node,fn,tree = <span class="literal">null</span>){</span><br><span class="line"><span class="comment">// 使用静态方法避免与 Array.prototype.map 混淆</span></span><br><span class="line">node.value = fn(node.value);</span><br><span class="line"><span class="keyword">if</span>(tree === <span class="literal">null</span>){</span><br><span class="line">tree = <span class="keyword">new</span> Tree(node);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(node.hasChildren()){</span><br><span class="line">_.map(node.children,<span class="function"><span class="keyword">function</span>(<span class="params">child</span>)</span>{</span><br><span class="line">Tree.map(child,fn,tree);</span><br><span class="line">})</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> tree;</span><br><span class="line">}</span><br><span class="line">get root(){</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">this</span>._root;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="第四章-模块化且可重用的代码"><a href="#第四章-模块化且可重用的代码" class="headerlink" title="第四章 模块化且可重用的代码"></a>第四章 模块化且可重用的代码</h3><p>Unix 的脚本程序的编写如下<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tr <span class="string">'A-Z'</span> <span class="string">'a-z'</span> < words.in | uniq | sort</span><br></pre></td></tr></table></figure></p><p>这行代码对字符进行可一系列的变换,大小写转换,去除排序等.管道操作符 | 用于连接这些命令.</p><ol><li>方法链接函数管道的比较<br>在 Haskell 中私有一种符号<code>::</code>来描述函数,如下:<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><function-name> :: <<span class="type">Input</span>*> -> <output></span><br></pre></td></tr></table></figure></li></ol><p>在函数式编程中,函数是输入和输出类型之间的数学映射.如 isEmpty 函数接收一个字符串并返回一个布尔值,使用该符号表示为:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// haskell 描述</span></span><br><span class="line">isEmpty :: <span class="built_in">String</span> -> <span class="built_in">Boolean</span></span><br><span class="line"><span class="comment">// js lambda 描述</span></span><br><span class="line"><span class="keyword">const</span> isEmpty = <span class="function"><span class="params">s</span> =></span> !s || !s.trim();</span><br></pre></td></tr></table></figure></p><p>链式调用<code>xxx.xxx().xxx()</code>虽然相比命令式代码提高了可读性,但是它与方法所属对象耦合在一起,只能使用由 Lodash 提供的操作,无法将不同函数库或自定义函数链接在一起.<br>而管道是松散结合的有向函数序列,一个函数的输出会作为下一个函数的输入.</p><ol><li>管道函数的兼容条件</li></ol><ul><li>类型: 函数的返回类型必须与接收函数的参数类型相匹配.</li><li>元数: 接收函数必须声明至少一个参数才能处理上一个函数的返回值.函数的参数长度和其复杂度成正比,只有一个单一参数的纯函数是最简单的,建议使用.但如何返回两个不同的值呢,函数式语言通过一个被称为元祖的类型达成.元组是不可变结构,将不同数据类型元素打包在一起,以便传递到其他函数中.如<code>(false,'error message')</code>.但 js 并不原生的支持 tuple 类型,在 es6 的解构赋值特性下可以简明的键元祖值映射到变量中.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[first,last] = [<span class="literal">false</span>,<span class="string">'error message'</span>];</span><br><span class="line">first <span class="comment">// false</span></span><br><span class="line">last <span class="comment">// error message</span></span><br></pre></td></tr></table></figure></li></ul><p>元祖是减少函数元数的方式之一,但还可以引入函数柯里化来实现降低元数的同时,增强代码模块化和可重用性.</p><ol><li>柯里化的函数求值<br>js 允许在确实产生的情况下对常规或非柯里化函数进行调用,js 会将缺少的参数设置为 undefined ,这或许也是 js 并不原生支持柯里化的原因.如果不设置行参,仅仅依靠 arguments 对象问题会更糟糕.<br>再看柯里化函数,它要求所有参数都被明确定义,当使用部分参数调用时,它会返回一个新的函数,在真正运行之前等待外部提供剩余参数.柯里化是一种在所有参数提供之前,挂起或延迟函数执行,将多参函数转换为一元函数序列的技术.<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 具有三个参数的柯里化定义</span></span><br><span class="line">curry(f) :: (a,b,c) -> f(a) -> f(b) -> f(c);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> add = <span class="function"><span class="params">x</span> =></span> y => <span class="function"><span class="params">z</span> =></span> x + y + z;</span><br></pre></td></tr></table></figure></li></ol><p>以上代码表明,curry 是一种从函数到函数的映射,将输入(a,b,c)分解为多个分离的单参数调用.<br>在纯函数式语言中,柯里化是原生特性,是任何函数定义中的组成部分.由于 js 不支持自动柯里化函数,需要编写一些代码来启用它.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 二元参数的手动柯里化</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">curry2</span>(<span class="params">fn</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">firstArg</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">secondArg</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> fn(firstArg,sencondArg);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如上所示,柯里化是一种词法作用域(闭包),其返回的函数只不过是一个接受后续参数的简单嵌套函数包装器.<br>像 lodash 一样,ramda.js 是一个函数式编程辅助库,之所以使用它是因为它很容易实现参数柯里化,惰性应用,和函数组合.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> checkType = curry2(<span class="function"><span class="keyword">function</span>(<span class="params">typeDef,actualType</span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(R.is(typeDef,actualType)){</span><br><span class="line"><span class="comment">// 使用 ramda 中 is()检查类型信息</span></span><br><span class="line"><span class="keyword">return</span> actualType;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'type mismatch'</span>)</span><br><span class="line">}</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">checkType(<span class="built_in">String</span>)(<span class="string">'Curry'</span>); <span class="comment">// String</span></span><br><span class="line">checkType(<span class="built_in">String</span>)(<span class="number">42</span>); <span class="comment">// type mismatch</span></span><br></pre></td></tr></table></figure></p><p>通过 R.curry 或 lodash 的curry 可以对任意数量参数的函数进行自动的柯里化.可以将自动柯里化想象为基于声明参数的数量而人工创建对应嵌套函数作用域的过程.</p><ol><li>部分应用(partial 偏函数)和函数绑定<br>部分应用是一种通过将函数的不可变参数子集初始化为固定值来创建更小元数函数的操作.简单说就是,如果存在一个具有五个参数的函数,给出三个参数后就会得到一个具有两个参数的函数.柯里化的函数本质上也是部分应用的函数.他们主要的区别在于参数传递的内部机制和控制.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//体积计算函数的部分应用</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">volume</span>(<span class="params">l</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">w, h</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> l * w * h</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>柯里化在每次分布调用时都会生成嵌套的一元函数,在底层函数的最终结果由这些一元函数逐步组合产生,所以可以完全控制函数求值的时间和方式.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// partial 的实现</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">partial</span> (<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">let</span> fn = <span class="keyword">this</span>, boundArgs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</span><br><span class="line"><span class="keyword">let</span> placeholder = <<partialPlaceholderObj>> // 占位符,lodash 使用下划线对象作为占位符,其他实现使用 undefined 来表示应略过该参数</span><br><span class="line">let bound = function(){</span><br><span class="line">// 使用部分参数创建新函数</span><br><span class="line">let position = 0,length= args.length;</span><br><span class="line">let args = Array(length);</span><br><span class="line">for(let i=0;i< length;i++){</span><br><span class="line">args[i] = boundArgs[i] === placeholder ? arguments[position++]: boundArgs[i]</span><br><span class="line">}</span><br><span class="line">while(positoion < arguments.length){</span><br><span class="line">args.push(arguments[positoion++])</span><br><span class="line">}</span><br><span class="line">return fn.apple(this,args)</span><br><span class="line">}</span><br><span class="line">return bound;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>部分应用将函数的参数与一些预设值绑定(赋值),从而产生一个拥有更少参数的新函数.该函数的闭包中包含这些已经赋值的参数,在之后的调用中完全被求值.一种类似的 js 原生技术被称为函数绑定,即 Function.prototype.bind()</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">_.partial(finc,[params...])</span><br><span class="line"><span class="comment">// 创建一个函数,该函数会调用 func,并传入预设的参数,与 _.bind 不同的是,它不会绑定 this.</span></span><br><span class="line"><span class="comment">// 例子</span></span><br><span class="line"><span class="keyword">var</span> greet = <span class="function"><span class="keyword">function</span>(<span class="params">greeting,name</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> greeting + <span class="string">' '</span> + name;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> sayHelloTo = _.partial(greet,<span class="string">'hello'</span>);</span><br><span class="line">sayHelloTo(<span class="string">'fred'</span>); <span class="comment">// hello fred</span></span><br></pre></td></tr></table></figure><ol><li><p>组合函数管道<br>我们来看一个例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> str = <span class="string">`we can only see a short</span></span><br><span class="line"><span class="string">distance</span></span><br><span class="line"><span class="string">three</span></span><br><span class="line"><span class="string">`</span></span><br><span class="line"><span class="keyword">const</span> explode = <span class="function"><span class="params">str</span> =></span> str.split(<span class="regexp">/\s+/</span>);</span><br><span class="line"><span class="keyword">const</span> count = <span class="function"><span class="params">arr</span> =></span> arr.length;</span><br><span class="line"><span class="keyword">const</span> countWords = R.compose(count,explode);</span><br><span class="line">countWords(str); <span class="comment">// 8</span></span><br></pre></td></tr></table></figure><p>这段程序有趣的地方在于,直到countWords被调用才会触发求值,用其名称传递的函数 explode 和 count 在组合中是静止的.这种<strong>将函数的描述和求值的行为分开</strong>正是函数式编程的强大之处.<br>我们来看一下 compose 的实现:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">compose</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">let</span> args = <span class="built_in">arguments</span>;</span><br><span class="line"><span class="keyword">let</span> start = args.length <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">let</span> i = start;</span><br><span class="line"><span class="keyword">let</span> result = args[start].apply(<span class="keyword">this</span>,<span class="built_in">arguments</span>);</span><br><span class="line"><span class="keyword">while</span>(i --){</span><br><span class="line">result = args[i].call(<span class="keyword">this</span>,result);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>使用 Ramda 这种函数库的好处就是所有函数都已经正确的柯里化,在组合函数管道时更具有通用性.<br>我们注意到 compose 函数是从参数最右到最左的顺序,而unix管道符 | 是从左到右执行的.我们可以使用 compose 的镜像函数 pipe 来获得 管道符一样的效果.不必像原来那样正式的声明参数来创建新的函数,函数式鼓励这种风格,它被称为 point-free.<br>point-free 使得 js 代码更接近 haskell 和 unix 的理念.柯里化能够灵活地定义一个只差最后一个参数的内联函数,这种编码风格被称为 Tacit 编程.</p></li><li><p>使用函数组合子来管理程序的控制流.<br>命令式代码能够加 if-else 和 for 语句这样的过程控制机制,而函数式则不能.<br>组合器是一些可以组合其他函数和组合子,作为控制逻辑运行的高阶函数.除了 compose 和 pipe,常见的组合子如下:</p></li></ol><ul><li>identity,意为身份,特性.它是返回与参数同值得函数.<br> <code>identity :: a -> a</code> 它广泛用于函数数学特性的检验</li><li>tap,意为轻拍.它能够将无返回值的函数嵌入到函数组合中,而无需创建其他代码.<br> <code>tap:: (a -> *) -> a -> a</code>该函数接受一个输入对象a 和一个对 a 执行操作的函数,使用提供的对象调用给定的函数,然后在返回该对象.</li><li><p>alternation,alt 组合子又叫 OR 组合子,能够在提供函数响应的默认行为时执行简单的条件逻辑.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> alt = <span class="function"><span class="keyword">function</span>(<span class="params">func1,func2</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">val</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> func1(val) || func2(val)</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>sequence,seq 组合子用于遍历函数序列,它以两个或以上的函数作为参数并返回一个新函数,会用相同的值顺序调用这些函数.实现如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> seq = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">const</span> funcs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">val</span>)</span>{</span><br><span class="line">funcs.forEach(<span class="function"><span class="params">fn</span> =></span> fn(val))</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>seq 不会返回任何值,只会一个一个的执行一系列操作.</p><ul><li>fork(join) 组合子<br>fork 用于需要以两中不同的方式处理单个资源的情况,该组合子需要以单个函数作为参数,即以一个 join 函数和两个 fork 函数来处理提供的输入,两个分叉函数的结果传递给join 函数.</li></ul><h3 id="第五章-针对复杂应用的设计模式"><a href="#第五章-针对复杂应用的设计模式" class="headerlink" title="第五章 针对复杂应用的设计模式"></a>第五章 针对复杂应用的设计模式</h3><ol><li><p>命令式错误处理的不足<br>在命令式编程中,异常都是通过 try-catch 处理的.将可能出现问题的代码放在 try 代码块中,通过 catch 捕获异常.但是,这样的代码将不能组合或连在一起,这将严重影响代码设计.<br><strong>函数式程序不应抛出异常</strong>,因为抛出异常会导致难以与其他函数组合,违反了引用透明原则,会引起副作用.</p></li><li><p>一种更好的解决方案Functor(函子).<br>通常我们在判断 null 和 undefined 时会写啰嗦且重复的的判断代码.函数式以一种完全不同的方法应对软件系统的错误处理,其思想就是创建一个安全的容器来存放危险代码.<br>functor 和 map 很类似,它会首先打开容器,应用函数到值,最后把返回的值包裹到一个新的同类型容器中.这种函数类型被称为 functor.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用 functor 完成 2 + 3 = 5</span></span><br><span class="line"><span class="keyword">const</span> plus = R.curry(<span class="function">(<span class="params">a,b</span>) =></span> a+b);</span><br><span class="line"><span class="keyword">const</span> plus3 = plus(<span class="number">3</span>);</span><br><span class="line"><span class="comment">// 将 2 放到warp容器中</span></span><br><span class="line"><span class="keyword">const</span> two = warp(<span class="number">2</span>);</span><br><span class="line"><span class="comment">// 调用 functor 把 plus3 映射到容器上</span></span><br><span class="line"><span class="keyword">const</span> five = two.fmap(plus3); <span class="comment">// Warpper(5) 返回一个具有上下文包裹的值</span></span><br><span class="line">five.map(R.identity) ; <span class="comment">// 5</span></span><br><span class="line"><span class="comment">// fmap 函数返回同类型的类型,可以链式调用</span></span><br><span class="line">two.fmap(plus3).fmap(R.tap(infoLogger)); <span class="comment">// 在控制台打印以下信息</span></span><br></pre></td></tr></table></figure><p>functor 是无副作用且可组合的,其实际目的只是创建一个上下文或一个抽象,以便可以安全的应用操作到值而不改变原始值.<br>函子是函数编程中最重要的数据类型,也是基本的运算单位和功能单元.一般约定,函子的标志就是容器拥有 map 方法,该方法将容器中的每一个值映射到另一个容器.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Functor</span></span>{</span><br><span class="line"><span class="keyword">constructor</span>(val){</span><br><span class="line"><span class="keyword">this</span>.val = val;</span><br><span class="line">}</span><br><span class="line">map(f){</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> Functor(f(<span class="keyword">this</span>.val))</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">(<span class="keyword">new</span> Functor(<span class="number">2</span>)).map(<span class="function"><span class="keyword">function</span>(<span class="params">two</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> two + <span class="number">3</span>;</span><br><span class="line">}) <span class="comment">// Functor(5)</span></span><br></pre></td></tr></table></figure></li></ol><p>还有一个更具体化的函数式数据类型 Monad,可以将电话代码中的错误处理,更流畅的进行函数组合,其实 Monad 就是 functor “伸入” 的容器.我们曾经写个这样的 jQuery 代码<code>$("#student").fadeIn(3000).text(student.fullname())</code>.jQuery 可以很安全的将 fadeIn 和 text 行为应用到 DOM 上,如果 student 的 id 不存在,方法会应用到空的 jQuery 对象上且什么也不发生,也不会抛出任何异常.Monad 在于安全的传送错误,这样代码才有较好的容错性.</p><ol><li>Monad 函数式的出路错误<br>我们来了解一下 functor 的局限性,当把两个 warp 包裹函数组合在一起的时候需要用两次 R.identity 函数来提取值,如果层数再多的话,monad 是更好的解决方案.</li></ol><p>假设有一个函数<code>half: number -> number</code>,<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Warpper(<span class="number">2</span>).fmap(half); <span class="comment">// warpper(1)</span></span><br></pre></td></tr></table></figure></p><p>functor只管应用到值并将结果包裹起来,并不能加额外的逻辑,如果想限制 half 只应用到偶数,而输入是一个奇数该怎么办.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isEven = <span class="function"><span class="params">n</span> =></span> <span class="built_in">Number</span>.isFinite(n) && (n%<span class="number">2</span> == <span class="number">0</span>);</span><br><span class="line"><span class="keyword">const</span> half = <span class="function">(<span class="params">val</span>) =></span> isEven(val) ? Wrap(val/<span class="number">2</span>) : empty();</span><br><span class="line"><span class="comment">// half 如果是一个奇数则返回一个空的容器</span></span><br></pre></td></tr></table></figure><p>Monad 用于创建一个带有一定规则的容器,而 Functor 不需要了解其容器内的值.使用 Monadic 类型需要了解以下定义:</p><ul><li>创建 Monadic 类型(类似于 Warpper的构造函数)</li><li>unit 函数,可将特点类型的值放入 Monadic 结构中,类似于 empty 函数.</li><li>bind 函数,可以链式操作,functor的 fmap</li><li>join 函数,将两层 monadic 结构合并为一层.用于逐层扁平化嵌套结构,无需多次提取.<br>Monad 函数的fmap 函数也叫 flatmap 函数,在大多数函数库里 flatmap 叫做 chain .</li></ul><p>下面来看丰富的 Monad 实例,maybe,Either 和 IO.<br>函数式编程通常使用 maybe 和 either 来隔离不纯,合并判空逻辑,避免异常,支持函数组合,中心化逻辑提供默认值.<br>简单说 maybe 函子的 map 方法里设置了空值检查.<br>Either 一般用来提供默认值.either 函子内部有两个值,left 和 right,right 是正常情况的值.left 是 right 不存在时的默认值.总之就是 right 有值用 right,否则用 left.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addOne = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> x + <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line">either.of(<span class="number">5</span>,<span class="number">6</span>).map(addOne); <span class="comment">// either(5,7)</span></span><br><span class="line">either.of(<span class="number">1</span>,<span class="literal">null</span>).map(addTOne); <span class="comment">// either(2,null)</span></span><br></pre></td></tr></table></figure></p><p>Either 另一个用途就是代替 try…catch,使用 left 表示错误.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">parseJson</span>(<span class="params">json</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> Either.of(<span class="literal">null</span>,<span class="built_in">JSON</span>.parse(json))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="第六章-可测试的函数式"><a href="#第六章-可测试的函数式" class="headerlink" title="第六章 可测试的函数式"></a>第六章 可测试的函数式</h3><p>略</p><h3 id="第七章-函数式优化"><a href="#第七章-函数式优化" class="headerlink" title="第七章 函数式优化"></a>第七章 函数式优化</h3><ol><li><p>函数执行机制<br>js 中,每个函数调用都会在函数上下文堆栈中创建记录(帧),它负责管理函数执行以及关闭变量作用域.<br>全局的上下文帧永远在堆栈的底部,函数体声明的变量越多,就需要越大的堆栈帧.<br>函数柯里化过度使用会导致其占有大量的堆栈空间,进而导致程序运行速度显著降低.<br>递归也会导致堆栈的溢出.因为递归时函数调用自己也会创建新的函数上下文,如果你见过<code>range error: Maximum call stack exceeded or too much recursion</code>就知道是递归出问题了.堆栈大小跟硬件也有关系.<br>既然大量函数推入堆栈会增加程序的内存占用,为什么不避免不必要的调用呢?</p></li><li><p>使用惰性求值推迟执行<br>函数式语言 Haskell 内置了惰性函数求值,惰性求值的方法有很多,但目的都是尽可能的推迟求值,直到依赖的表达式被调用.<br>但是 js 使用的是更主流的函数求值策略 - 及早求值,它会在表达式绑定到变量时求值,不管结果是否用到,也称贪婪求值.</p></li></ol><p>2.1 使用函数式组合子避免重复计算.alt 组合子类似于 || 运算,先计算 func1 如果返回值为 假,在调用 func2.这是避免不必要计算的简单方法,还有一个更强大的方法 memoization.<br>2.2 函数式编程的 shortcut fusion(意为: 捷径 融合),是一种函数级别的优化,它通过合并函数执行,并压缩计算过程中使用的临时数据结构有效降低内存占用.之所以可以这样做事因为函数式编程引用透明带来的数学和代数的正确性.<br>比如 compose(map(f),map(g))可以由 map(compose(f,g))完全代替.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> square = <span class="function"><span class="params">x</span> =></span> <span class="built_in">Math</span>.pow(x,<span class="number">2</span>)</span><br><span class="line"><span class="keyword">const</span> isEven = <span class="function"><span class="params">x</span> =></span> x%<span class="number">2</span> === <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> numbers = _.range(<span class="number">200</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = _.chain(numbers)</span><br><span class="line">.map(square)</span><br><span class="line">.filter(isEven)</span><br><span class="line">.take(<span class="number">3</span>) <span class="comment">// 仅处理前三个</span></span><br><span class="line">.value() <span class="comment">// [0,4,16]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// map 和 filter 可以通过 compose 融合在一起</span></span><br><span class="line"><span class="string">``</span><span class="string">` </span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">2.3 记忆化 memorization</span></span><br><span class="line"><span class="string">加快程序执行的方法之一就是避免计算重复值,在传统的面向对象中,通过将函数结果赋予给唯一的键值对并持久化到缓存中.</span></span><br><span class="line"><span class="string">而在函数式中记忆化是一种很好的方式.它基于函数的参数创建与之对应的唯一的键,将结果存储到键上,当再次遇到相同的参数的函数时,立即返回储存的结果.</span></span><br><span class="line"><span class="string">给 FUnction 添加记忆化</span></span><br><span class="line"><span class="string">`</span><span class="string">``</span>js</span><br><span class="line"><span class="built_in">Function</span>.prototype.memoized = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">let</span> Key = <span class="built_in">JSON</span>.stringify(<span class="built_in">arguments</span>);<span class="comment">//将参数字符串化以获取当前函数调用的键值</span></span><br><span class="line"><span class="keyword">this</span>._chace = <span class="keyword">this</span>.cache || {}; <span class="comment">// 为当前函数实例创建一个内部缓存</span></span><br><span class="line"><span class="keyword">this</span>._chace[key] = <span class="keyword">this</span>._chace[key] || <span class="keyword">this</span>.apply(<span class="keyword">this</span>,<span class="built_in">arguments</span>)<span class="comment">// 先试图读取缓存,通过输入判断是否计算过,找到就离开返回,没找到这开始计算</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">this</span>._chace[key]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">Function</span>.prototype.memoize = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="comment">// 激活函数记忆化</span></span><br><span class="line"><span class="keyword">let</span> fn = <span class="keyword">this</span>;</span><br><span class="line"><span class="keyword">if</span>(fn.length === <span class="number">0</span> || fn.length><span class="number">1</span>){</span><br><span class="line"><span class="keyword">return</span> fn; <span class="comment">// 只尝试记忆化一元函数</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">return</span> fn.memoized.apply(fn,<span class="built_in">arguments</span>)</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>设计多个参数的函数即使是纯函数也很难缓存,因为复杂度增加了,柯里化是解决方案之一.<br>递归和尾递归优化,es6 添加的尾部调用消除,可以再递归调用时不依赖当前帧,创建一个新的帧并回收旧的帧.</p>]]></content>
<summary type="html">
<h1 id="JavaScript-函数式编程"><a href="#JavaScript-函数式编程" class="headerlink" title="JavaScript 函数式编程"></a>JavaScript 函数式编程</h1><h3 id="第一章-走进函数式
</summary>
<category term="JavaScript" scheme="http://yoursite.com/categories/JavaScript/"/>
<category term="JavaScript 函数式编程" scheme="http://yoursite.com/tags/JavaScript-%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/"/>
</entry>
<entry>
<title>读<<上帝的骰子>></title>
<link href="http://yoursite.com/2020/06/15/%E8%AF%BB%3C%3C%E4%B8%8A%E5%B8%9D%E7%9A%84%E9%AA%B0%E5%AD%90%3E%3E/"/>
<id>http://yoursite.com/2020/06/15/读<<上帝的骰子>>/</id>
<published>2020-06-15T11:43:05.000Z</published>
<updated>2021-08-30T11:46:55.861Z</updated>
<content type="html"><![CDATA[<h1 id="读-lt-lt-上帝的骰子-gt-gt"><a href="#读-lt-lt-上帝的骰子-gt-gt" class="headerlink" title="读<<上帝的骰子>>"></a>读<<上帝的骰子>></h1><h3 id="量子力学的前夜"><a href="#量子力学的前夜" class="headerlink" title="量子力学的前夜"></a>量子力学的前夜</h3><p>中学时我们就学过牛顿三定律。<br>牛顿第一定律即惯性定律:不受外力的物体将在惯性系中保持静止或匀速直线运动的状态不变。接着,他又给出,说明力、质量和运动之间的定量关系:物体的加速度与它所受的外力成正比,与它的质量成反比。牛顿第三定律则指出:两个物体间的作用力和反作用力大小相等,方向相反,作用在一条直线上。也就是说,牛一定律说明了力是改变物体运动状态的原因;牛二定律指出了力使物体获得加速度;牛三定律揭示了力是物体间的相互作用。除了牛顿三定律,再加一个万有引力定律。牛顿完成了经典力学架构,统一了万物运行背后的道理。</p><p>大伙儿都相信牛顿定律就是宇宙的终极真理,宏观世界很快热闹了起来。科学家们真干实干加巧干,一直干到20世纪,终于建成了一座宏观物理学大厦。经典力学、热力学、光学、电磁学等在大厦里各司其职。<br>眼看科研经费一年比一年少,前途一片黯淡。这时,哥本哈根学派的一帮年轻科学家开始掀桌子。宏观世界是没什么活儿可以干了,但是还有微观世界啊!牛顿力学只适用于宏观世界,可一旦深入微观世界,比如原子级别,这套理论就完全找不着北了。那量子力学到底是怎样诞生的呢?人类在研究光的过程中偶然邂逅了无辜的量子。因此我们的故事得追溯到一个古老的问题——光是什么?</p><p>从光的本质说起很久很久以前,人类祖宗的祖宗就在思考:这世界到底是由什么构成的?古希腊哲人再一次展示出他们惊人的物理直觉:光由一粒一粒非常小的光原子所组成。就这样,微粒说一直统治着上古科学界。直到17世纪初,它才迎来宿敌——波动说。它率先由失恋的数学教授格里马第提出,这个失恋的男人躲在小黑屋里疯狂地做实验。让一束光穿过两个小孔后,他恍惚看到旧情人眼里水波的流动。一瞬间他顿悟了,这不正是一种衍射现象吗?最后,他哭闹着向全世界宣布:光是一种波。1663年左右,英国科学家胡克加入波动学说的阵营中。一开始波动派挺高兴,总算盼来一位猛将。一向视胡克为死对头的牛顿发话了:既然你胡克支持波动说,那——<br>1672年,牛顿发布光的色散实验,矛头直指波动说要害。1704年,他发大招出版《光学》一书。并在序言中写下:为了避免对这些论点的无谓争论,我推迟了这部书的公开发行。波动说阵营群龙无首,无人应战。</p><p>直到一个世纪后——才有一位少年天才敢站到牛顿的对立面,为波动说站台。托马斯·杨。作为物理学五大经典实验之一,在一个月黑风高之夜,天才杨开始了表演:他点燃了一支蜡烛。直接点燃了量子革命的火种,留下了一条历史性的干涉条纹……</p><h3 id="旧量子论的奠基"><a href="#旧量子论的奠基" class="headerlink" title="旧量子论的奠基"></a>旧量子论的奠基</h3><p>麦克斯韦预言光是电磁波的一种,迈出了史诗级的一步。可这预言是对是错,终究得有个人来给它证明。这个人,就是麦克斯韦的弟子—— 赫兹。1887年,赫兹通过一个高频振荡回路,证明了电磁波的存在。这个实验确认了光的波动性。电磁理论的一体化,标志着经典物理达到了顶峰。揭示电磁波存在的同时,赫兹的实验还出现了一个奇怪的现象:光电效应。</p><p>什么是光电效应?就是在高于某频率的电磁波的照射下,某些物质的电子会被光子激发出来,从而形成电流,即光生电。光能转化成电能,物质的电性质由此发生变化……宏观世界的理论无法解释光电效应,后来我们才知道:光电效应的背后,是科学家要研究的新方向。那是一个人类一直不曾进入的世界——微观量子世界。普朗克、爱因斯坦和玻尔这三位奠基者,也马上要登场了。</p><p>1900年,普朗克在研究黑体辐射时大胆假定:能量在发射和吸收时,不是连续不断的,而是一份一份的。这个不连续假设,正是量子理论最初的萌芽。就这样,普朗克稀里糊涂地提出了量子概念。它推翻了微积分几百年的连续基础,开始挖牛顿世界的墙角。大家普遍将1900年12月14日,普朗克发表《论正常光谱的能量分布定律的理论》的这一天当作量子物理学诞生的日子。然而,能量子的概念太激进了!面对这样一个骇人的真相,这个老派绅士被自己吓得魂飞魄散。但提出者无心,研究者有意。</p><p>1905年,听墙角的爱因斯坦开始收割普朗克的劳动果实。天才的直觉告诉爱因斯坦,对于光来说,量子化可能是一种必然的选择。他在普朗克的假设上提出,光以量子的形式存储能量,不累积。一般情况下,一个量子打出一个电子,这就是著名的光量子效应。按照爱因斯坦的理论,光又成了粒子,具有不连续性。但比起旗帜鲜明地站队光到底是微粒还是波,爱因斯坦更在乎自己的直觉。光具有波粒二象性。</p><p>对于20世纪初的科学家来说,你说光既是波又是粒子,这怎么可能?粒子是单个存在的个体,而波则是集体运动的结果,这两者根本不可能统一啊。借此机会,微粒说率先开始了绝地反击。1923年,康普顿看到了光,开始带领微粒军大举反攻。他大胆引入光量子假设,完成了X射线散射实验,光的粒子性被证实。可微粒派还没来得及露出得意的笑容。1923年,法国贵族王子德布罗意出场了。为了阻止微粒说和波动说一触即发的大战,德布罗意从光量子理论中顿悟到:正像光波可以表现为粒子一样,粒子也可以表现为波!不仅仅是光,一切物质都具有波粒二象性。这就是物质波理论。全世界的物理大师都保持沉默,只有爱因斯坦一个人点赞支持德布罗意。就这样,场面一度僵持。</p><p>结果,还没等大家从德布罗意的物质波理论冲击中回过神来——<br>1925年4月,戴维逊和革末进行的电子衍射实验发现:电子居然表现出波动性质!电子居然是个波!这下,波动和微粒双方阵营都炸开了锅。1925年,正当物理学陷入十字路口时,24岁的海森伯出现了,他被认为是微粒派的代表。他试图用数学来解释微观粒子运动。最后,他选择了一种不符合交换率的古怪矩阵来描述量子理论。在玻恩、约尔当和狄拉克的助攻下,很快,海森伯的矩阵力学就在旧量子系统废墟上建立了起来。可好景不长,薛定谔加入了战斗。他被认为是波动派的代表。他嫌矩阵力学太装,故弄玄虚让大家都看不懂。他认为,是微粒还是波,这根本没那么复杂,量子性不过是微观体系波动性的反映。只要把电子看成德布罗意波,用一个波动方程表示电子运动即可。</p><p>他就这样提出了名震20世纪物理学史的薛定谔波函数。看到熟悉的微分方程,那些被海森伯矩阵整得晕头转向的大佬,个个热泪盈眶。毫不犹豫,他们转身就把矩阵力学打入了冷宫。一边是骄傲的海森伯,一边是好胜的薛定谔。一边是以微粒说为基础的矩阵力学,一边是以波动说为基础的波函数。</p><p>矩阵力学和波动力学,从此成了生死天敌。尴尬的是,1926年4月,薛定谔、泡利、约尔当各自证明:两种力学在数学上来说是完全等价的!</p><p>搞了半天,不过是同一理论的不同表达形式而已。两座大厦其实建立在同一地基上:微观粒子的波粒二象性。但旧量子论真正的集大成者,不是普朗克,也不是爱因斯坦,而是来自丹麦的玻尔。1913年,他发表了三篇论文:《论原子和分子的构造》《单原子核体系》《多原子核体系》。这三篇论文成为物理学经典之作,被称为玻尔模型三部曲。用对应原理算出了氢原子能级。经过普朗克、爱因斯坦、玻尔三大先行者的接力,旧量子论终于从牛顿宏观理论的阴影里爬了出来。但这时的人们最多只是刚爬到微观世界的门口,新量子论(即真正意义上的量子力学)仍处于混沌之中。</p><h3 id="量子力学的建立"><a href="#量子力学的建立" class="headerlink" title="量子力学的建立"></a>量子力学的建立</h3><p>普朗克、爱因斯坦、玻尔三人接力救了旧量子论。但真正建立量子力学(新量子论)国度的开国元勋却来自哥本哈根学派。他们的主将有三个:玻恩、海森伯、玻尔(没错,又有玻尔)。</p><p>玻恩算是海森伯的半个老师。他是一名地地道道的物理教授,在哥廷根开了个理论班。海森伯就是在那里跟着玻恩搞科研的。1926年,海森伯哭着跑回家说,他被薛定谔欺负了。在矩阵力学和波动力学被证明等价后的尴尬中,他们两人表面休战,薛定谔却暗中使绊子,到处骂矩阵力学变态。本就高冷难追的矩阵力学,风头远远被薛定谔的波函数盖了过去。玻恩气得肝疼,发誓一定要替自家弟子报仇。他找上了远在哥本哈根的大哥玻尔,准备联合起来找回面子。1926年7月,薛定谔接受玻尔的邀请前往哥本哈根,正春风得意的薛定谔,并未察觉这是一场鸿门宴。</p><p>在他赞美着自己的波函数时,护徒心切的玻恩出手了——玻恩先假仁假义地夸赞了对方一番,再挖了个坑:阁下波函数中的ψ,代表什么?毫无警觉的他,笑呵呵地解释:ψ函数代表电子电荷在空间中的实际分布。玻恩反驳,不,电子本身不会像波那样扩展,而是它的概率分布像一个波。ψ函数代表的不是实际位置,而是电子在某个地点出现的一种随机概率。玻恩很淡定。他以子之矛,攻子之盾,用对方的一个波动实验给出了最好的证明:电子双缝干涉实验。</p><p>电子穿过两道狭缝后,便形成了一个明暗相间的图案,也就是干涉条纹。一个电子究竟出现在哪儿,我们无法确定。连这个世界都是以概率形式存在的,我们只能预言概率。一切都只是随机的?玻恩,你这是在挑战整个科学的决定论根基!借助电子双缝干涉实验,玻恩狠狠扇了薛定谔一个大耳光。但还没等玻恩开心多久,哥本哈根学派自家后院先着火了。 1927年,大哥玻尔改变了对波动力学的看法。当初为了赢薛定谔,他也没少研究波动说,可里里外外解剖完,玻尔突然觉得,这也是个好东西。</p><p>要不试试,把波动说当做量子论的基础,看能不能搞个新理论出来?1927年,闹别扭的海森伯还在跟矩阵较劲。他试图用矩阵来对抗薛定谔方程。在绞尽脑汁的思考过程中,他突然想起:矩阵其实是不符合小学的乘法交换律的!最后研究得出不确定性原理.玻恩的随机概率解释已经让人头大了。这次海森伯更狠,他直接否定了物理学。这是一种哲学上的原则问题。不仅是你波动说,不管你创立什么理论,都必须服从不确定性原理!</p><p>可外界还是不服气。照你们的说法,电子是波也是微粒,不确定性是电子在波和微粒之间的一种随机表现。可你们又没同时见过电子波和电子粒,谁能做证?玻尔急中生智,直接抢白:谁说电子是波又是微粒,就一定能同时观察到两种状态了?为了听上去更有说服力,玻尔还进行了官方陈词总结,这就是互补原理。波和粒子在同一时刻是互斥的.</p><p>概率解释、不确定性原理、互补原理就这样颠覆了人们对宇宙的终极认识。它们共同构成了量子论哥本哈根解释的核心。概率解释与不确定性原理摧毁了世界的因果性,不确定性原理和互补原理合力干掉了世界的绝对客观性。</p><h3 id="爱因斯坦和波尔的战争"><a href="#爱因斯坦和波尔的战争" class="headerlink" title="爱因斯坦和波尔的战争"></a>爱因斯坦和波尔的战争</h3><p>爱因斯坦认为,量子这熊孩子已经长歪了,哥本哈根学派的解释,根本就没有办法说服他。这个当初提出光量子理论的男人,是因果律和客观性的坚定拥护者,却对量子力学(新量子论)嗤之以鼻孔。哥本哈根学派欺负了自己的小弟薛定谔,爱因斯坦决定找个机会好好教训一下他们。哥本哈根派与爱因斯坦总共约架三次。正是这三次约架,奠定了量子力学在物理学上的重要地位,使它成为20世纪最伟大的两大理论之一。</p><p>1927年10月24日,第五届索尔维会议召开。这是他们的第一次约架。看热闹的不少,整个物理学界能排得上号的人基本都来了。爱因斯坦、玻尔、薛定谔、德布罗意、玻恩、普朗克、朗之万,狄拉克、居里夫人……29个人,其中有17个人是诺贝尔奖的获得者!这群人组成了一支物理学全明星梦之队,留下了堪称人类历史上智商巅峰的一张合影。就算不是绝后,也一定是空前的。这支全明星梦之队分为三个阵营:一个是哥本哈根学派,以玻尔为首。成员有海森伯、玻恩、泡利、狄拉克……第二个阵营是他们的老对手,以爱因斯坦为首的反对派。麾下有抱大腿的薛定谔、小王爷德布罗意等几员大将。还有一个闲云野鹤派,他们不在乎你们谁和谁打架,只关心实验结果。<br>最前头站着的是布拉格和康普顿,身后还站着居里夫人、德拜等一群看热闹不嫌事儿大的人。</p><p>德布罗意小王爷一马当先,提出导波的概念,试图推翻概率解释,用因果关系解释波动力学。他说,我虽然提出了物质波,但你们都没搞懂。粒子是波动方程的一个奇点,就像波上的一个包,它必须受波的引导。而这个波,其实就是物质的运动轨迹。导波没有物质波幸运,它遭到了泡利的猛烈反击。被称为上帝之鞭的泡利从小就是个暴脾气。身为海森伯的师兄,他对他们的老师也照样尖刻。极具个性的他,一言不合就丢出一个泡利不相容原理(在费米子组成的系统中不能有两个和两个以上的粒子处于完全相同的状态)如果波是物质的运动轨迹,那你倒是说说,这个运动到底是怎么回事,德布罗意小王爷羞红了脸,下不来台。薛定谔想来助阵,结果自身难保。他的电子云理论被玻恩和海森伯两师徒前后夹击。薛定谔认为,波是真实存在的,电子在空间中的实际分布如波般扩散,就像一团云。可海森伯很嚣张:对不起啊,从你的计算中,我看不到任何可以证明你理论的东西。薛定谔自知自己的计算还不完善,便硬着头皮还击,那你们提出的什么波本征态叠加更胡扯!以一敌二,薛定谔直接被玻恩、海森伯怼到怀疑人生。</p><p>眼看自己的两大亲兵节节败退,在一阵可怕的沉默中,爱因斯坦终于爆发了。他直接提出一个模型:一个电子通过一个小孔得到衍射图像。假设一片隔板中间有一条狭缝,朝着这隔板的狭缝发射一个电子,发射的方向垂直于隔板,电子穿过了狭缝,再移动一段距离后,抵达感应屏障。没错,你们的概率分布是比薛定谔的电子云完备。但你们说,电子在到达感应屏前都不确定,到达的一瞬间概率就变成了100%?这种随机性不是要以超距作用为前提吗?这是违背相对论的!爱因斯坦是神一般的人物,是大当家的玻尔的偶像。面对身为反方带头大哥的爱因斯坦,玻尔勇敢地站了出来。你这个模型,同样不能避免测量时仪器对电子不可控的相互作用,即电子与狭缝边沿的相互作用,电子在通过A缝时如果不超距怎么感知旁边没有其他的缝呢?</p><p>也就是说,其实你这个模型也是符合量子理论的,你还要反驳我们吗?玻尔出招,虽然重剑无锋,但直取对方致命弱点。爱因斯坦想反驳,可憋了半天,愣是没憋出一个字。会场鸦雀无声……第一个回合,哥本哈根学派胜出。低估了对手实力,爱因斯坦很不服气。他又提出一个模型:电子双缝干涉实验。若控制装置,让某一时刻只有一个粒子穿过,并分别关闭狭缝,就可以测出电子的准确路径和位置。而由干涉条纹又可计算电子波的波长,从而可精确确定电子的动量。怎么样,这下你们的测不准关系被否定了吧?爱因斯坦自以为这局一定稳胜,可玻尔却古怪地笑了:爱因斯坦先生,如果你关上其中任何一个狭缝,实验的状态就完全改变了!双缝开启干涉现象也不再出现,实验又回到了单缝状态,等于又多了一次不确定因素!这个实验,不但没反驳成功互补原理,反而用互补原理说明了波粒二象性!第二回合,还是哥本哈根学派胜!六天的会议,变成了这两个人的对台戏。爱因斯坦屡战屡败却越挫越勇。最后,他恼羞成怒,扔下了一句物理学名言:玻尔,上帝不掷骰子!玻尔此时也已经豁出去了,他毫不留情地回呛:爱因斯坦,别去指挥上帝该怎么做!第一次爱玻之战,以爱因斯坦的惨败告终。</p><p>1930年,第六届索尔维会议召开。这是他们的第二次约架。这次,爱因斯坦有备而来。他先发制人,快准狠地打出一张实验牌:光箱子。箱子里有n个光子,时间间隔Δt之后打开箱子,每次只放出一个光子,Δt确定。再用理想的弹簧秤测出箱子的质量,发现轻了Δm,将Δm代入质能方程E=mc2,ΔE也确定。既然ΔE和Δt都确定,那你们家不确定性原理,ΔEΔt>h,也就不成立!玻尔毫无思想准备,当场蒙了。第二天一大早,一夜没合眼的玻尔,顶着两个浓重的黑眼圈出现在台上。好,你说一个光子跑了,箱子轻了Δm,这没问题。那怎么测量这个Δm呢?广义相对论中的红移效应,即光频率降低的现象。引力场可以使原子的频率变低,也就是红移,等效于时间变慢。你想要准确测量Δm或ΔE,可你其实根本没办法控制光子逃出的时间Δt,它测不准。爱因斯坦哑口无言。苦心孤诣三年,他和薛定谔、德布罗意在小黑屋反复沙盘推演,原以为万无一失、可以一招制敌。可自己精心设计的实验,又一次成了不确定性原理的一个绝佳例证。第二次约架,爱因斯坦又输了!</p><p>1933年,第七届索尔维会议召开。可彼时,爱因斯坦正被纳粹逼得在异国他乡流浪,他缺席了。缺了爱因斯坦,会议变得索然无味。丢了主心骨的薛定谔、德布罗意两人,在新量子论的喧闹中沉默不语。1935年,孤独的爱因斯坦又找到了两个同盟军,波多尔斯基和罗森,他们联合发表了一篇论文。论文的名字特别长,叫《量子力学对物理实在的描述可能是不完备的》。</p><p>这一次,是双方的第三次约架。爱因斯坦吸取了之前血的教训。他不再攻击量子力学的正确性,而准备改说它是不完备的。对于量子力学,爱因斯坦心理上有两个坎儿过不去。一个是,怎么可能有超光速信号的传播?爱因斯坦称之为定域性。另外一个是实在性:你不去看,难道天上的月亮就不存在了吗?爱因斯坦准备了一个实验,来说明量子力学违背了定域实在论,大意是:一个母粒子分裂成两个自旋方向相反的子粒子A和B。这两个粒子是互相影响的。如果粒子A为左旋,那B一定是右旋,以保持总体守恒,反之亦然。</p><p>按照量子力学的解释,这两个粒子相互之间是有联系的。那么,如果这两个粒子分开足够远——比如,粒子A在银河系的这头,粒子B在银河系的那头,相隔10万光年以上。你对粒子A吹口气,难道粒子B也会在一瞬时做出相对的反应吗?(这两个纠缠态的粒子,薛定谔成为量子纠缠,)因此,量子力学并不完备!</p><p>综上所述,这就是整篇论文的论据。这个思想实验,也被称为EPR佯谬,命名灵感来自三人名字的缩写。玻尔淡定地给出了反击——你二话不说就先假定了两个粒子在观察前,分别都有个客观的自旋状态存在。这两个客观存在的粒子是哪儿来的?根据量子力学的理论,在没有观测前,一个客观独立的世界并不存在,更不存在客观独立的两个粒子。它们本就是一个相互联系、相互影响的整体。在被观测之后,粒子A、粒子B才变成客观真实的存在。我们两个前提都不一样,量子力学仍然是完备、逻辑自洽的。不得不说,爱因斯坦是一个伟大的反对派。作为一代科学巨匠,他的反对成了量子力学最好的试金石,每一次他提出的问题,都推动量子力学前进了一大步。甚至有人怀疑他是量子力学派来的卧底。1962年,玻尔去世后的第二天——人们在他的黑板上,发现了当年爱因斯坦光箱子的实验草图。他对爱因斯坦的反对是如此眷恋,至死还萦绕于心。而此时的爱因斯坦,已经去世了7年。</p><h3 id="薛定谔的猫"><a href="#薛定谔的猫" class="headerlink" title="薛定谔的猫"></a>薛定谔的猫</h3><p>在爱因斯坦的光环下,薛定谔虽然只是小弟,但自身同样也是实力一流的大科学家。哥本哈根学派第一条核心原理——概率诠释,就是用薛定谔方程来描述量子行为。虽然不怎么喜欢他这个反对党,但哥本哈根派也不得不承认薛定谔是量子力学的奠基人之一。除此之外,薛定谔还是分子生物学的开山鼻祖,他写的《生命是什么》一书畅销至今。</p><p>薛定谔的猫是怎么来的呢?爱因斯坦落败后,老薛心里极度憋屈又扭曲。他又一次复习了EPR理论,觉得没毛病啊!薛定谔认为爱因斯坦没有错,错的是哥本哈根学派,这一派个个都是诡辩高手。他得再做一个实验,这个实验要让每个人一眼就看懂。正想着实验怎么做的薛定谔扫了一眼周围——他的猫正在撕咬他的论文《量子力学的现状》!气不打一处来的薛定谔灵光乍现:这么皮,把你拿去做实验好了!薛定谔把猫放进一个不透明的盒子里。<br>盒子连接到一个包含放射性原子核和有毒气体的实验装置中。可怜的猫被活生生关在里面。如果原子衰变了,毒气瓶会被打破,盒子里的猫会被毒死。要是原子没有衰变,猫就好好地活着。根据量子力学理论,原子核处于衰变和未衰变的叠加态。那么这只猫理所当然也随着原子核叠加进入一种又死又活的状态。这样一只猫,与我们的常识是如此相悖。</p><p>薛定谔得意地大笑:玻尔,你们见过一只又死又活的猫吗?薛定谔的猫思想实验的高超之处在于:它将看不见的微观世界与可视化的宏观世界联系了起来。这只猫,成了行走于宏观世界和微观世界的灵宠。你们不是欺负人们看不到吗?我现在就让全世界看到你们哥本哈根学派的丑陋!你们非要将我的波函数方程解释成粒子的一种叠加概率波。你看,现在搬起石头砸自己的脚了吧!叠加态不是微观世界量子论的核心吗?现在我将它带到宏观世界了,你们自己看看,它是多么可笑!薛定谔的猫实验否定的是哥本哈根学派的概率解释。如果量子力学的三大基石之一被毁掉了,那科学家进军微观世界的梦想将彻底破灭。</p><p>首先给出解释的,还是哥本哈根学派。哥本哈根学派其实心里也有点虚,但他们只能硬着头皮上:你的实验盒子里,有一个计数器是用来测量原子是否衰变的。从这一步测量开始,波函数的叠加态就已经坍缩了。后面的猫是生是死,完全是属于经典世界的,不存在叠加态。可不久,现代应用计算机鼻祖,年青的冯·诺伊曼就一针见血地指出:不对!计数器本身也是由微观粒子组成的!你用B去测量A,用C去测量B,只不过是A的叠加态转移到了B,B的不确定又转移到了C……到最后,整个大系统的波函数还是没有坍缩。到最后,波函数之所以坍缩,还是因为人的意识参与。只要没有被意识到,猫就是又死又活的。可究竟什么是意识?大脑?灵魂?思想?这种解释太唯心主义了。</p><p>暗中窥视的爱因斯坦一派伺机而动。看到量子力学大厦被意识决定论搞得摇摇欲坠,他们悄悄带来了第二种解释,也就是反哥本哈根学派的诠释。他们不反对量子力学,只想在量子力学的世界抢班夺权,掠取哥本哈根学派打下来的量子江山。它的代表人是玻姆。1952年,玻姆创立了一个完整的隐变量体系。在玻姆看来,哥本哈根学派含糊混淆的那些现象,主要是因为存在着一个隐形变量。为此,他用高超的数学手法复活了导波。写下了一个复杂得让许多科学家觉得生无可恋的隐函数。玻姆说,这个隐变量,就是爱因斯坦寻找的神秘力量。但因为我们还没有发现,也发现不了,所以微观粒子才表现出不确定,才会有叠加态。(奥拉姆剃刀原则,即简单有效原则,如果同一种现象有两种或多种不同的假说,我们应该采取最简单或可证伪的)虽然看上去特别有道理,但不能证伪,玻姆的隐函数同样难以服众!这明显违反了奥卡姆剃刀原则。</p><p>1957年,又一个不走寻常路的家伙出现了:埃弗莱特。他带来了荒谬又可笑的第三种解释。他大大咧咧地说,别多愁善感了,根本没有什么又死又活的叠加猫,猫也不是你看一眼就死了的。本来就有两只猫,一只是活着的,另一只死了。只不过这两只猫各自在两个世界里,两个你看到了不同的猫。埃弗莱特眼中有一个量子世界:整个宇宙是一个总体的波函数叠加系统,里面包含了很多个完全孤立、互不干涉的子世界。从宇宙大爆炸以来,这些世界就各自演化着,谁也看不到谁。这就是平行宇宙解释.(Many Worlds Interpretation,简称MWI)。</p><p>一群继承了多宇宙思想的科学家。他们在MWI基础上发展出了一种新的解释:退相干。这种新解释,就是第四种解释,也是目前的主流解释。它解释了MWI中为何平行世界没有在宏观中显示叠加态。通俗点来说,就是解释了为什么我们感受不到另外一个平行世界。退相干的理论研究者首先指出,不可能有同时又死又活的猫。如果猫是活的,那一步步反推回去,毒气瓶就没有碎,放射性原子也没有衰变,反之同理。也就是说,如果猫不生死叠加,那放射性原子也是不叠加的,波函数早就坍缩了。那波函数是什么时候坍缩的?又是什么东西导致它坍缩的?<br>这群人给出的答案是:无论是薛定谔的盒子,还是整个宏观世界,都是由无数微观粒子组成的。它们的叠加性其实也是一种相干性。但量子的相干性会因外部环境的干涉而逐渐消失。说白了,就是其他粒子影响了盒子里的放射性原子,最后变成宏观性质了。量子退相干是德国学者汉斯在1970年提出的。但和可怜的埃弗莱特一样,当时并没有多少人注意到它。直到1984年,哈特尔的关注才让退相干理论正式发展壮大起来。退相干历史认为在宇宙中世界只有一个,但历史有很多个,分为粗粒历史、精细历史。精细历史是量子历史,无法求解概率,粗粒历史是经典历史,在宏观上显示,类似于路径积分,可以计算概率。每一个粒子都处在所有精细历史的叠加中,比如放射性原子。但一旦涉及宏观物体,我们所能观察到的就是一些粗粒化的历史,比如打开盒子后看到的猫。因为量子退相干了,这些历史永久地失去了联系,只剩一种被我们感知到。本该是粒子叠加态的薛定谔实验,打开盒子后,就只能看到一种状态的猫(生/死)。虽然退相干并不是十全十美,但无论是从数学上还是哲学上,它都让三维世界的我们好受一点。现在它已经成为量子力学的主流理论之一。不少科学家正利用它来建立真正的现实应用。量子计算与量子通信就正在与退相干做斗争。</p><p>薛定谔本来想让他的猫恶心哥本哈根学派,嘲讽一下量子力学。结果他到死也没想到,他的猫竟然成了量子世界的鲇鱼。只能说,薛定谔不愧是爱因斯坦的小弟,连给量子力学送助攻,都和爱因斯坦一模一样。爱因斯坦提出的EPR佯谬像不可攻破的堡垒。尽管在量子风暴中饱受摧残,它的定域实在论仍然牢牢把守着经典世界的大门。哪怕爱因斯坦曾三次落败。可直到去世,他心底里其实也没被玻尔说服。这两个伟大科学家之间的较量,早就超越了个人之间的战争,是一场关于世界本质的辩论。</p><p>微观世界到底符合定域实在论(经典),还是量子不确定性?最终一定要做一个了断。1964年,爱因斯坦的信徒——贝尔,重温了EPR佯谬。把定域实在论转化为另一种令所有科学家心服口服的语言。他提出了一个不等式——(|pxz-pzy| <= 1+pxy)这个不等式用超越了宇宙文明维度的数学语言铸就而成,被称为科学史上最深刻的发现。既然在物理世界没办法决出高下,我们就转战到更本质的数学领域,用数学来判断究竟谁对谁错。</p><h3 id="贝尔不等式"><a href="#贝尔不等式" class="headerlink" title="贝尔不等式"></a>贝尔不等式</h3><p>贝尔不喜欢量子力学听上去主观又唯心的一套。他想要的是一个确定的、客观的世界。贝尔有自己隐藏的绝招,那就是1952年玻姆提出的隐函数。在新一代大神冯·诺伊曼的禁锢中,隐变量举步维艰。可贝尔坚持认为,隐变量是反击哥本哈根学派的大杀器。玻姆的隐变量抛弃了定域性,但它至少恢复了世界的实在性。只要他在这基础上再证明一个定域隐变量的存在,就证明了量子力学的非定域性也是错的。他撸起袖子,研究起了爱因斯坦的老实验:EPR佯谬。在EPR佯谬理论中,一个母粒子分裂成了两个自旋方向相反的子粒子A和B。按照爱因斯坦一派关于隐变量的思想,两个子粒子A和B,就像南北极的两只手套。不管你观测不观测,它们是左手还是右手,从分开那时起就已经确定了。既然宇宙中不存在超距作用那么,在观测的一瞬间,两个纠缠的粒子必然在经典世界存在某种极限。假设Pxy是粒子A在x方向上和粒子B在y方向上的相关性,Pzy、Pxz同理,则可得出:(|pxz-pzy| <= 1+pxy),这个不等式对宇宙的本质做出了最后的裁决。</p><p>它意味着,如果我们的世界同时满足:1.定域的,也就是没有超光速信号的传播。2.实在的,也就是说,存在着一个独立于我们观察的外部世界。那么两个具有相反自旋方向的粒子,它们的运动,必定受限于不等式。简单来说,就是——如果微观世界是经典的,那么不等式成立。反之,则不成立。这个由隐变量理论推导出来的式子,它打破了一直以来的僵局,隐变量重见天日,一个定域又实在的世界近在眼前。物理学家开始骚动起来,他们按捺不住,想要亲身参与到大结局中。在数学与好奇心的撩拨下,他们纷纷动手改造起了EPR佯谬思想模型,做起了贝尔不等式实验。1972年,有个叫克劳泽的小厮成功实现了实验。这是史上第一个验证贝尔不等式的实验。不过,结果让贝尔魂飞天外——那两个纠缠的粒子,竟然突破了贝尔不等式??!这意味着,真的存在鬼魅般的量子纠缠?贝尔心心念念的微观世界经典性竟然是错的。</p><p>1982年,在巴黎奥赛光学研究所,又一场惊心动魄、万众瞩目的实验正在进行,这一次所有人都屏住了呼吸。这次的实验领导人是正在读博士的阿斯派克特。不同于克劳泽的幼稚版装置,阿斯派克特的技术非常成熟。借助激光的强信号源,一对对光子从钙原子中冲出,朝着偏振器奔去,它们关乎整个量子力学的命运。在令人窒息的24个小时的等待后,结果 出来了:实验再一次与贝尔想要的结果相反,玻尔是对的,爱因斯坦又一次输了!世界再也不可能回到那个美好的经典时代了。</p><p>数学是物理学的基石,贝尔不等式用严谨的数学手段覆灭了整个爱因斯坦军团,EPR实验最终成了EPR佯谬。数学的降维打击助力量子力学取得了胜利。在克劳泽和阿斯派克特之后,还有一大批追求完美的科学家也进行了实验。从5倍偏差,到9倍偏差,再到30倍偏差……模型越来越完备,技术越来越精密,都证明了玻尔是对的。多年爱玻之争,终于在宇宙判决书贝尔不等式中画上了句号。</p><p>此后量子力学的追随者开始分成两拨继续探索。一拨是勤耕不辍的理论派。他们一直试图深入微观世界,甚至想统一整个宇宙。为了达成这个长期目标,理论派把宇宙划分为4种力:电磁作用力、强相互作用力、弱相互作用力、引力。通过这4种力,一切物理现象都可以得到解释。天才科学家们找到了一种大一统理论,先用它将前三种属于量子力学的基础作用力都装进去,剩下一种属于广义相对论的引力,他们寄希望于更前沿的弦理论。弦理论认为,自然界的基本单元不是传统意义上的点状粒子。而是很小很小的橡皮筋一样的线状弦。当我们用不同的方式弹橡皮筋,它就会振动,产生自然界中的各种粒子,可能是电子、光子,也可能是引力子。这样,引力就有望被微观量子化描述,和前三种力统一在一起。微观(量子力学)和宏观(广义相对论)也就有望统一了。<br>除了有着远大抱负的理论派外,另外一拨量子力学的追求者是实践派。这是一群实用主义者,他们挖掘出一项又一项伟大的量子应用。没有它,我们就不会有CD、DVD、蓝光影碟播放器;没有它,也不会有晶体管、智能手机、电脑、卫星导航;没有它,更不会有激光、电子显微镜、原子钟、核磁共振显示装置……</p><p>有了量子力学,人类便进入了一个新时代。</p>]]></content>
<summary type="html">
<h1 id="读-lt-lt-上帝的骰子-gt-gt"><a href="#读-lt-lt-上帝的骰子-gt-gt" class="headerlink" title="读&lt;&lt;上帝的骰子&gt;&gt;"></a>读&lt;&lt;上帝的骰子&gt;&gt;</h1
</summary>
<category term="读书" scheme="http://yoursite.com/categories/%E8%AF%BB%E4%B9%A6/"/>
<category term="科普 物理 量子力学" scheme="http://yoursite.com/tags/%E7%A7%91%E6%99%AE-%E7%89%A9%E7%90%86-%E9%87%8F%E5%AD%90%E5%8A%9B%E5%AD%A6/"/>
</entry>
<entry>
<title>Chrome 插件开发指南</title>
<link href="http://yoursite.com/2020/05/10/Chrome%20%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/"/>
<id>http://yoursite.com/2020/05/10/Chrome 插件开发指南/</id>
<published>2020-05-10T03:47:49.000Z</published>
<updated>2021-08-30T09:48:58.468Z</updated>
<content type="html"><![CDATA[<h1 id="Chrome-插件开发指南"><a href="#Chrome-插件开发指南" class="headerlink" title="Chrome 插件开发指南"></a>Chrome 插件开发指南</h1><h3 id="开发与调试"><a href="#开发与调试" class="headerlink" title="开发与调试"></a>开发与调试</h3><p>chrome插件没有严格的项目结构要求,只有保证本目录有一个 <code>manifest.json</code> 即可,从<code>浏览器菜单-更多工具-扩展程序</code>可以进入插件管理页面。或直接输入地址 <a href="https://www.notion.so/chrome-extensions-09ff4f8b5fb448d7addd8727b9fa90cb" target="_blank" rel="external">chrome://extensions</a>访问。</p><p>勾选开发者模式可以用文件夹的形式直接加载插件,否则只能安装.crx 格式的文件。<br>mac 系统下插件安装目录为: <code>~/Library/Application Support/Google/Chrome/Default/Extensions</code></p><h3 id="核心介绍"><a href="#核心介绍" class="headerlink" title="核心介绍"></a>核心介绍</h3><ol><li><p>manifest.json</p><p> 用来配置插件相关的配置信息,必须放在根目录。且以下属性是必不可少的。完整属性可以查看<a href="https://developer.chrome.com/extensions/manifest" target="_blank" rel="external">官方文档</a>。</p> <figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="string">"manifest_version"</span> : <span class="number">2</span>,</span><br><span class="line"><span class="string">"name"</span> : <span class="string">"test"</span>,</span><br><span class="line"><span class="string">"version"</span> : <span class="string">"1.0.0"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>content-scripts (插件与页面交互)</p><p> 是 chrome 插件向页面注入脚本的一种形式,我们可以通过manifest.json配置轻易的向页面注入 js 和 css,最常见的是广告屏蔽,页面样式定制等等.</p> <figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="string">"content-scripts"</span> : [</span><br><span class="line">{</span><br><span class="line"><span class="string">"matches"</span> : [<span class="string">"http://*/*"</span>, <span class="string">"<all_urls>"</span>],</span><br><span class="line"><span class="string">"js"</span>: [<span class="string">"js/xxx.js"</span>,<span class="string">"....js"</span>],</span><br><span class="line"><span class="string">"css"</span>: [<span class="string">"css/xx.css"</span>],</span><br><span class="line"><span class="string">"run_at"</span>: <span class="string">"document_start"</span> <span class="comment">// 可选 document_start/end/idle(默认空闲)</span></span><br><span class="line">}</span><br><span class="line">]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> content-scripts 与原始页面共享DOM,但不共享JS,如果想要访问页面JS某个变量,只能通过 injected js 来实现,content-scripts不能访问绝大部分chrome.xxx.api, 除了以下四种。</p><pre><code>- chrome.extension(getURL,inIncognitoContext, lastError,onRequest,sendRequest)- chrome.i18N- chrome.runtime(connect,getManifest,getURL,id, onConnect,onMessage,sendMessage)- chrome.storage</code></pre></li><li><p>background<br> 后台是一个常驻的页面,它随着浏览器的开关而开关.通常把需要一直运行的代码放在 background 里面.<br> 他的权限非常高,可以调用 chrome 的扩展 API(除了 devtools), 而且它可以无限制跨域.配置中,background 可以通过 page 指定一个页面,也可以通过 scripts 指定一个 js,chrome 会自动为这个 js 生成一个默认页面.</p> <figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="string">"background"</span>:{</span><br><span class="line"><span class="string">"page"</span>: <span class="string">"xxx.html"</span></span><br><span class="line"><span class="comment">// scripts: ["js/xxx.js"]</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>event-pages<br> 鉴于 background 生命周期和浏览器同步,长时间挂载后台影响性能,而 event-pages 与 background 唯一的区别就是多了一个 persistent 参数,它会在需要时被加载,空闲时被关闭.一般 background 用的比较多.</p></li><li><p>popup<br> popup 是点击插件图标时打开的一个窗口网页,焦点离开网页就关闭,一般做一些交互使用.<br> popup 可以包含任意你想要的 HTML,并且会自适应大小,可以通过 default_popup 来指定页面,也可以调用 setPopup()方法.</p> <figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"browser_action"</span> : {</span><br><span class="line"><span class="string">"default_icon"</span>: <span class="string">"img/xx.png"</span>,</span><br><span class="line"><span class="string">"default_title"</span>: <span class="string">"悬停时的标题"</span>,</span><br><span class="line"><span class="string">"default_popup"</span>: <span class="string">"xx.html"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> popup 的生命和周期很短,需要长时间运行的代码不要放在 popup 里.popup 中可以通过 chrome.extension.getBackgroundPage() 获取 background 的 window 对象.</p></li><li><p>injected-script<br> content-script 无法访问页面中的 js,虽然它可以操作 dom,但 dom 却不能调用它,也就是在 dom 的事件中无法调用 content-script 中的代码,但是在页面中添加一个按钮并调用插件的 api 是很常见的需求,我们可以再 content-script 中通过 DOM 方式向页面注入 inject-script.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// content-script</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">injectCustomJs</span>(<span class="params">jsPath</span>)</span>{</span><br><span class="line">jsPath = jsPath || <span class="string">'js/inject.js'</span>;</span><br><span class="line"><span class="keyword">var</span> temp = <span class="built_in">document</span>.creatElement(<span class="string">"script"</span>);</span><br><span class="line">temp.src = chrome.extension.getURL(jsPath); <span class="comment">// 类似于 chrome-extension://xxxx/js/inject.js</span></span><br><span class="line">temp.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">this</span>.parentNode.removeChild(<span class="keyword">this</span>);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">document</span>.head.appendChild(temp);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 代码会报错,因为在 web 中直接访问插件中的资源必须显示声明才行,在配置文件中增加以下配置:</p> <figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="comment">// 普通页面能够直接访问的插件资源列表,不设置无法直接访问.</span></span><br><span class="line"><span class="string">"web_accessible_resources"</span>: [<span class="string">"js/inject.js"</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> inject-script 如何调用 content-script 中的代码,要用到消息通信.</p></li><li><p>homepage_url<br> 开发者网站</p></li></ol><h3 id="Chrome-插件的-8-种展示形式"><a href="#Chrome-插件的-8-种展示形式" class="headerlink" title="Chrome 插件的 8 种展示形式"></a>Chrome 插件的 8 种展示形式</h3><ol><li><p>browserAction 浏览器右上角图标</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="string">"browser_action"</span> : {</span><br><span class="line"><span class="string">"default_icon"</span>: <span class="string">"img/xx.png"</span>, <span class="comment">// 19*19 </span></span><br><span class="line"><span class="string">"default_title"</span>: <span class="string">"悬停时的标题"</span>,</span><br><span class="line"><span class="string">"default_popup"</span>: <span class="string">"xx.html"</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 通过 setIcon 更改 icon, setTitle()更改鼠标 hover 时的标题.setBadgeText()来更改图标上的文本信息.</p></li><li><p>pageAction 地址栏右侧<br> 当某些特定页面打开时会在地址栏右边显示的图标.(新版吧位置放到了浏览器右边,可以把它看成置灰的 browserAction.<br> 例如当打开百度时才显示图标.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// background.js</span></span><br><span class="line">chrome.runtime.onInstalled.addListener(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line">chrome.declarativeContent.onPageChanged.removeRules(<span class="literal">undefined</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line">chrome.declarativeContent.onPageChanged.addRules([</span><br><span class="line">{</span><br><span class="line">conditions: [</span><br><span class="line"><span class="comment">// 只有打开百度才显示pageAction</span></span><br><span class="line"><span class="keyword">new</span> chrome.declarativeContent.PageStateMatcher({<span class="attr">pageUrl</span>: {<span class="attr">urlContains</span>: <span class="string">'baidu.com'</span>}})</span><br><span class="line">],</span><br><span class="line">actions: [<span class="keyword">new</span> chrome.declarativeContent.ShowPageAction()]</span><br><span class="line">}</span><br><span class="line">]);</span><br><span class="line">});</span><br><span class="line">});</span><br></pre></td></tr></table></figure></li><li><p>右键菜单<br> 通过 chrome.contextMenus API,右键菜单可以出现在不同的上下文.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// manifest.json</span></span><br><span class="line">{<span class="string">"permissions"</span>: [<span class="string">"contextMenus"</span>]}</span><br><span class="line"><span class="comment">// background.js</span></span><br><span class="line">chrome.contextMenus.create({</span><br><span class="line">title: <span class="string">"测试右键菜单"</span>,</span><br><span class="line">onclick: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{alert(<span class="string">'您点击了右键菜单!'</span>);}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p> <a href="https://developer.chrome.com/extensions/contextMenus" target="_blank" rel="external">常见 API 参考</a> </p></li><li><p>覆盖特定页面<br> 使用 override 页可以将 chrome 默认的一些特定页面替换掉.一个插件只能替代一个默认页.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"chrome_url_overrides"</span>:</span><br><span class="line">{</span><br><span class="line"><span class="string">"newtab"</span>: <span class="string">"newtab.html"</span>,</span><br><span class="line"><span class="string">"history"</span>: <span class="string">"history.html"</span>,</span><br><span class="line"><span class="string">"bookmarks"</span>: <span class="string">"bookmarks.html"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>devtools(开发者工具)<br> 例如 vue.js devtools , chrome 可以再 devtools 上新增一个面板.<br> devtools 页面会随着开发者工具的开关而开关.可以访问 Devtools API ,而其他比如 background 无权访问.</p><ul><li>chrome.devtools.panels: 面板相关</li><li>chrome.devtools.inspectedWindow: 获取被审查窗口的信息</li><li><p>chrome.devtools.network: 获取有关网络请求的信息</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="string">"devtools_page"</span>: <span class="string">"XXX.html"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个 html 一般什么都没有,只有一个 script 标签引用 js 文件<code><script src='js/devtools.js'></script></code>,<br>再看一下 devtools 的代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建自定义面板,同一个插件可以创建多个自定义面板</span></span><br><span class="line"><span class="comment">// 几个参数依次为:panel标题、图标(其实设置了也没地方显示)、要加载的页面、加载成功后的回调</span></span><br><span class="line">chrome.devtools.panels.create(<span class="string">'MyPanel'</span>, <span class="string">'img/icon.png'</span>, <span class="string">'mypanel.html'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">panel</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'自定义面板创建成功!'</span>); <span class="comment">// 注意这个log一般看不到</span></span><br><span class="line">});</span><br><span class="line"><span class="comment">// 创建自定义侧边栏</span></span><br><span class="line">chrome.devtools.panels.elements.createSidebarPane(<span class="string">"Images"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">sidebar</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="comment">// sidebar.setPage('../sidebar.html'); // 指定加载某个页面</span></span><br><span class="line">sidebar.setExpression(<span class="string">'document.querySelectorAll("img")'</span>, <span class="string">'All Images'</span>); <span class="comment">// 通过表达式来指定</span></span><br><span class="line"><span class="comment">//sidebar.setObject({aaa: 111, bbb: 'Hello World!'}); // 直接设置显示某个对象</span></span><br><span class="line">});</span><br><span class="line"><span class="comment">// 访问被检查的页面DOM需要使用inspectedWindow</span></span><br><span class="line">chrome.devtools.inspectedWindow.eval(<span class="string">"jQuery.fn.jquery"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">result, isException</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">var</span> html = <span class="string">''</span>;</span><br><span class="line"><span class="keyword">if</span> (isException) html = <span class="string">'当前页面没有使用jQuery。'</span>;</span><br><span class="line"><span class="keyword">else</span> html = <span class="string">'当前页面使用了jQuery,版本为:'</span>+result;</span><br><span class="line">alert(html);</span><br><span class="line">});</span><br></pre></td></tr></table></figure></li></ul></li><li><p>选项页 option<br> 选项页是插件的设置页面,有两个入口,一个是右键图标菜单,一个是插件管理页面.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="comment">//"options_page": "options.html" ,(老版本写法)</span></span><br><span class="line"><span class="string">"options_ui"</span>: {</span><br><span class="line"><span class="string">"page"</span>: <span class="string">"optionxxx.html"</span>,</span><br><span class="line"><span class="string">"open_in_tab"</span>: <span class="literal">true</span>, <span class="comment">// 在当前 tab 打开</span></span><br><span class="line"><span class="string">"chrome_style"</span>: <span class="literal">true</span> <span class="comment">// 添加了一些默认样式</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 不能使用 alert, 数据存储建议使用 chrome.storage, 因为会随用户自动同步</p></li><li><p>omnibox<br> 注册某个关键字触发插件自己的搜索建议界面.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"><span class="comment">// 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字</span></span><br><span class="line"><span class="string">"omnibox"</span>: { <span class="string">"keyword"</span> : <span class="string">"go"</span> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 然后在 background 注册监听事件:</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">chrome.omnibox.onInputChanged.addListener(<span class="function">(<span class="params">text, suggest</span>) =></span> {</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'inputChanged: '</span> + text);</span><br><span class="line"><span class="keyword">if</span>(!text) <span class="keyword">return</span>;</span><br><span class="line"><span class="keyword">if</span>(text === <span class="string">'xxx'</span>){</span><br><span class="line">suggest([</span><br><span class="line">{<span class="attr">content</span>: <span class="string">'百度搜索 '</span> + text, <span class="attr">description</span>: <span class="string">'百度搜索 '</span> + text},</span><br><span class="line">{<span class="attr">content</span>: <span class="string">'谷歌搜索 '</span> + text, <span class="attr">description</span>: <span class="string">'谷歌搜索 '</span> + text},</span><br><span class="line">]);</span><br><span class="line">}</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>桌面通知:<br> chrome 提供了一个 chrome.notification API 方便插件推送桌面通知.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">chrome.notifications.create(<span class="literal">null</span>,{</span><br><span class="line">type: <span class="string">"basic"</span>,</span><br><span class="line">iconUrl: <span class="string">"img/xx.ping"</span>,</span><br><span class="line">title: <span class="string">"标题"</span>,</span><br><span class="line">message: <span class="string">"内容"</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure></li></ol><h3 id="消息通信"><a href="#消息通信" class="headerlink" title="消息通信"></a>消息通信</h3><ul><li><p>popup 和 background<br> popup 可以直接调用 background 的 js 方法,也可以访问 background 的 DOM.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// background.js</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>)</span>{</span><br><span class="line">alert(<span class="string">"background"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// popup.js</span></span><br><span class="line"><span class="keyword">var</span> bg = chrome.extension.getBackgroundPage();</span><br><span class="line">bg.test();</span><br><span class="line">bg.document.body.innerHTML; <span class="comment">// dom</span></span><br></pre></td></tr></table></figure></li><li><p>popup 或 bg 向 content 发消息</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bg,popup.js</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sendMessageToContent</span>(<span class="params">message,callback</span>)</span>{</span><br><span class="line">chrome.tabs.query({<span class="attr">active</span>:<span class="literal">true</span>,<span class="attr">currentWindow</span>: <span class="literal">true</span>},<span class="function"><span class="keyword">function</span>(<span class="params">tabs</span>)</span>{</span><br><span class="line">chrome.tabs.sendMessage(tabs[<span class="number">0</span>].id,message,<span class="function"><span class="keyword">function</span>(<span class="params">res</span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(callback) callback(res);</span><br><span class="line">})</span><br><span class="line">})</span><br><span class="line">}</span><br><span class="line"><span class="comment">// content-script.js 接收</span></span><br><span class="line">chrome.runtime.onMessage.addListener(<span class="function"><span class="keyword">function</span>(<span class="params">req,send,res</span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(req.cmd == <span class="string">'test'</span>) alert(req.value);</span><br><span class="line">send(<span class="string">"我收到了你的消息"</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>content 向 bg/popup 主动发消息</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// content-script.js</span></span><br><span class="line">chrome.runtime.sendMessage({</span><br><span class="line">greeting: <span class="string">"hello"</span></span><br><span class="line">}, <span class="function"><span class="keyword">function</span>(<span class="params">res</span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"收到回复"</span> + res)</span><br><span class="line">})</span><br><span class="line"><span class="comment">// bg 或 popup.js</span></span><br><span class="line">chrome.runtime.onMessage.addListener(<span class="function"><span class="keyword">function</span>(<span class="params">req,send,res</span>)</span>{</span><br><span class="line">send(<span class="string">"我收到了你的消息"</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>injected script 和 content script<br> content-script 和页面内 脚本(injected-script)之间唯一共享的就是页面 DOM 元素.有两种方式实现通信,一是通过 window.postMessage 和 window.addEventListener 实现消息通信(推荐),二是通过 自定义 dom 事件.</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// injected - script</span></span><br><span class="line"><span class="built_in">window</span>.postMessage({<span class="string">"test"</span>: <span class="string">'hello'</span>},<span class="string">'*'</span>);</span><br><span class="line"><span class="comment">// content- script</span></span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">"message"</span>,<span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(e.data)</span><br><span class="line">},<span class="literal">false</span>)</span><br></pre></td></tr></table></figure></li><li><p>长连接和短连接<br>chrome 插件中有两种通信方式,一种是短连接(chrome.tabs.sendMessage和 chrome.runtime.sendMessage),一个是长连接(chrome.tabs.connect 和 chrome.runtime.connect).</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 长连接</span></span><br><span class="line"><span class="comment">// popup.js</span></span><br><span class="line"><span class="keyword">var</span> port = chrome.tabs.connect(tabId,{<span class="attr">name</span>: <span class="string">'test-connect'</span>});</span><br><span class="line">port.postMessage({<span class="attr">xxx</span>:<span class="string">'xxx'</span>});</span><br><span class="line">port.onMessage.addListener(<span class="function"><span class="keyword">function</span>(<span class="params">msg</span>)</span>{</span><br><span class="line">alert(<span class="string">'收到消息'</span> + msg.answer)</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">// content-script</span></span><br><span class="line">chrome.runtime.onConnect.addListener(<span class="function"><span class="keyword">function</span>(<span class="params">port</span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(port === <span class="string">'test-connect'</span>){</span><br><span class="line">port.onMessage.addListener(<span class="function"><span class="keyword">function</span>(<span class="params">msg</span>)</span>{</span><br><span class="line">alert(<span class="string">"收到长连接"</span>,msg)</span><br><span class="line">})</span><br><span class="line">}</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li></ul><h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><ul><li><p>获取当前窗口 id</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">chrome.windows.getCurrent(<span class="function"><span class="keyword">function</span>(<span class="params">cw</span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(cw.id)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>获取当前标签页 id</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getCurrentTabId</span>(<span class="params">callback</span>)</span>{</span><br><span class="line">chrome.tabs.query({<span class="attr">active</span>:<span class="literal">true</span>,<span class="attr">currentWindow</span>:<span class="literal">true</span>},<span class="function"><span class="keyword">function</span>(<span class="params">tabs</span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(callback) callback(tabs.length ? tabs[<span class="number">0</span>].id : <span class="literal">null</span>)</span><br><span class="line">})</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>定期执行代码</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 配置</span></span><br><span class="line"><span class="string">"permissions"</span>: [<span class="string">"alarms"</span>]</span><br><span class="line"><span class="comment">// 创建方法</span></span><br><span class="line">chrome.alarms.create(name,info);<span class="comment">// name 为任务名, info 包含以下属性 when 何时,dalayInMinutes 延迟时间, periodInMinutes 非 null表示时间间隔,单位 min</span></span><br><span class="line">chrome.alarms.onAlarm.addListener(xxx); <span class="comment">// 触发事件</span></span><br></pre></td></tr></table></figure></li><li><p>本地存储<br>chrome.storage 是针对插件全局的,即使在 background 中保存的数据,在 content-script 也能获取到.chrome.storage.sync 可以跟随当前登录用户自动同步.需要声明 storage 权限,有 sync 和 local 两种方式选择.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 读取数据,第一个参数是要读取的 key 以及默认值</span></span><br><span class="line">chrome.storage.sync.get({<span class="attr">color</span>: <span class="string">'red'</span>,<span class="attr">age</span>:<span class="number">18</span>},<span class="function"><span class="keyword">function</span>(<span class="params">items</span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(items)</span><br><span class="line">})</span><br><span class="line"><span class="comment">// 保存数据</span></span><br><span class="line">chrome.storage.sync({<span class="attr">color</span>:<span class="string">'blue'</span>},<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'save success'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>快捷键唤醒 popup</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"commands"</span>: {</span><br><span class="line"><span class="string">"_execute_browser_action"</span>:{</span><br><span class="line"><span class="string">"suggested_key"</span>:{</span><br><span class="line"><span class="string">"default"</span>: <span class="string">"Alt+Shift+J"</span> <span class="comment">// 快捷键唤醒</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>webRequest<br>通过 webrequest API 可以对 HTTP 请求进行修改</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//权限申请</span></span><br><span class="line"><span class="string">"permissions"</span>: [</span><br><span class="line"><span class="string">"webRequest"</span>, <span class="comment">// web请求</span></span><br><span class="line"><span class="string">"webRequestBlocking"</span>, <span class="comment">// 阻塞式 web 请求</span></span><br><span class="line"><span class="string">"storage"</span>,<span class="comment">// 插件本地储存</span></span><br><span class="line"><span class="string">"http://*/*"</span> <span class="comment">//可以通过 executeScript 或 insertCss 访问的网站</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment">// web 请求监听</span></span><br><span class="line">chrome.webRequest.onBeforeRequest.addListener(<span class="function"><span class="params">details</span> =></span>{</span><br><span class="line"><span class="keyword">let</span> showImage = <span class="literal">false</span> ; <span class="comment">// 不展示图片</span></span><br><span class="line"><span class="keyword">if</span>(!showImage && details.type === <span class="string">'image'</span>){</span><br><span class="line"><span class="keyword">return</span> {</span><br><span class="line">cancel: <span class="literal">true</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(details.type === <span class="string">'media'</span>){</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">}</span><br><span class="line">},{<span class="attr">urls</span>: [<span class="string">"<all_urls>"</span>]},[<span class="string">"blocking"</span>]);</span><br></pre></td></tr></table></figure></li><li><p>国际化<br>插件根目录新建一个_locales 的文件夹,在新建一些语言文件夹如 en,zh_CN,zh_TW,然后在每个语言文件夹放入一个 messages.json,同时在文件中设置 default_locale.测试时,通过给chrome建立一个不同的快捷方式chrome.exe –lang=en来切换语言</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// en/message.json</span></span><br><span class="line">{</span><br><span class="line"><span class="string">"pluginDesc"</span>: {<span class="string">"message"</span>: <span class="string">"A simple chrome extension demo"</span>},</span><br><span class="line"><span class="string">"helloWorld"</span>: {<span class="string">"message"</span>: <span class="string">"Hello World!"</span>}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// zh_CN/message.json</span></span><br><span class="line">{</span><br><span class="line"><span class="string">"pluginDesc"</span>: {<span class="string">"message"</span>: <span class="string">"一个简单的Chrome插件demo"</span>},</span><br><span class="line"><span class="string">"helloWorld"</span>: {<span class="string">"message"</span>: <span class="string">"你好啊,世界!"</span>}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 在 manifest.json 和 css 文件中通过 __MSG_messagename__引入</span></span><br><span class="line">{</span><br><span class="line"><span class="string">"description"</span>: <span class="string">"__MSG_pluginDesc__"</span>,</span><br><span class="line"><span class="comment">// 默认语言</span></span><br><span class="line"><span class="string">"default_locale"</span>: <span class="string">"zh_CN"</span>,</span><br><span class="line">}</span><br><span class="line"><span class="comment">// js 中使用</span></span><br><span class="line">chrome.i18n.getMessage(<span class="string">"helloWorld"</span>)</span><br></pre></td></tr></table></figure></li></ul>]]></content>
<summary type="html">
<h1 id="Chrome-插件开发指南"><a href="#Chrome-插件开发指南" class="headerlink" title="Chrome 插件开发指南"></a>Chrome 插件开发指南</h1><h3 id="开发与调试"><a href="#开发与调
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="chrome 插件" scheme="http://yoursite.com/tags/chrome-%E6%8F%92%E4%BB%B6/"/>
</entry>
<entry>
<title>solidity 区块链编程入门</title>
<link href="http://yoursite.com/2020/04/11/solidity%20%E5%8C%BA%E5%9D%97%E9%93%BE%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8/"/>
<id>http://yoursite.com/2020/04/11/solidity 区块链编程入门/</id>
<published>2020-04-11T11:31:16.000Z</published>
<updated>2021-08-30T11:32:44.517Z</updated>
<content type="html"><![CDATA[<h1 id="solidity-区块链编程入门"><a href="#solidity-区块链编程入门" class="headerlink" title="solidity 区块链编程入门"></a>solidity 区块链编程入门</h1><h3 id="编译器"><a href="#编译器" class="headerlink" title="编译器"></a>编译器</h3><p>Remix编译器 可以在线使用或离线使用.<br>使用 node 可以安装 solidity 编译器 solcjs<br>mac 可以通过homebrew 安装 编译器 solidity</p><h3 id="源文件结构"><a href="#源文件结构" class="headerlink" title="源文件结构"></a>源文件结构</h3><p><code>pragma solidity ^0.5.2;</code> 表示版本号及编辑器版本<br><code>import * as symbolName from "filename";</code>导入其他源文件</p><h3 id="值类型"><a href="#值类型" class="headerlink" title="值类型"></a>值类型</h3><ol><li><p>整数类型分为 int/uint 定义. 可以显式设置占用空间大小,默认是 int256/uint256.</p></li><li><p>定长浮点类型: fixed/ufixed 表示各种大小有符号和无符号的定长浮点型.分别是 fixed128x19 和 ufixed128x19 的别名,第一个数字表示占用的位数,必须是 8 的倍数,第二个数字是可用的小数点位.</p></li><li><p>布尔型bool 分为 true 和 false</p></li><li><p>运算符与 javascript 相同,除 === 之外.</p></li><li><p>以太坊地址为 160 位即 20 字节大小.用 address 表示地址类型.地址有两种 address payable 可以接受以太币,而 address 则不行.前者可以隐式转换为普通地址,但普通地址要想转换为 payable 必须通过 payable()函数.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">address public owner; <span class="comment">// 定义地址</span></span><br><span class="line">owner.balance; <span class="comment">//查看余额</span></span><br><span class="line">addressA.transfer(<span class="number">1</span> ether)<span class="comment">// 像 A 转 1 eth,地址无效或余额不足会抛出异常.</span></span><br><span class="line">addressA.transfer.gas(<span class="number">120000</span>)(<span class="number">1</span> ether) <span class="comment">// 转账 附带 gas 的写法</span></span><br><span class="line">owner.send(<span class="number">1</span> ether) <span class="comment">// send 是 transfer 的低级版本,有风险,合约失败返回 false,建议使用 transfer</span></span><br></pre></td></tr></table></figure></li><li><p>每一个 contract 合约都有自己的类型,可以显式的转换为 adress 类型,只有当合约具有 receive(接收) 函数或 payable 回退函数时,才能显式和 address payable 类型相互转换.转换仍然使用 address()执行,如果没有接收函数和回退函数需要用 payable(address(x))转换为 address payable.<br> 对于合约可以使用 type(xx) 来获取合约的类型信息. </p></li></ol><ol><li>固定长字节数组,以 bytes 加数字表示,如 bytes2 表示 两个字节长度的数组,数组范围为 1-32.默认是 1.<br> 动态长度字节数组分两种,bytes 和 string(不支持索引访问)</li></ol><h3 id="引用类型"><a href="#引用类型" class="headerlink" title="引用类型"></a>引用类型</h3><ol><li><p>如果使用引用类型,必须明确数据存储在哪个位置.<br> 变量的储存位置有三种,memory 修饰的变量储存在内存中仅在函数运行期有效不能外部调用, storage 修饰的变量存储在区块链上只有合约存在就有效,calldata 指调用数据,用来保存函数参数,是一个只读位置.<br> 函数返回值默认是 memory,函数局部变量的默认数据是 storage,状态变量的默认数据是 storage.</p></li><li><p>数组截图在声明时指定长度,也可以动态调整.push()添加一个元素,返回对它的引用. 同理还有 pop 函数.<br> bytes 和 string 也是数组.string 不能使用索引, bytes 等同于 byte[] 但 gas 消耗更低.可以使用 new 关键字创建内存数组,但不能改变其内存数组的大小.<br> solidity 提供数组切片 x[start:end],仅仅可用于 calldata</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">uint[][<span class="number">5</span>] x = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>];</span><br><span class="line">x[<span class="number">0</span>] = <span class="number">6</span>; <span class="comment">// x 为[6,2,3,4,5]</span></span><br><span class="line">x.length = <span class="number">5</span>;</span><br><span class="line">uint[] y = [<span class="number">1</span>,<span class="number">2</span>] <span class="comment">// 动态长度</span></span><br><span class="line">xxxtype[] public xxxx; <span class="comment">// 自定义 xx 类型数组</span></span><br><span class="line">bytes memory b = <span class="keyword">new</span> bytes(<span class="number">9</span>)</span><br><span class="line">uint[<span class="number">3</span>][<span class="number">5</span>] x; <span class="comment">//与大多数编程语言相反,为 5 行 3 列.</span></span><br></pre></td></tr></table></figure><ol><li>结构体是自定义数据类型,可以是字符串整型等基础类型,也可以是数组映射结构体等复杂类型.可以使用关键字 struct 定义.<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义</span></span><br><span class="line">struct Bank{</span><br><span class="line">address owner;</span><br><span class="line">uint balance;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 初始化方法一</span></span><br><span class="line">Bank b = Bank({</span><br><span class="line">owner: msg.sender,</span><br><span class="line">balance: <span class="number">5</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">// 方法 2</span></span><br><span class="line">Bank c = Bank(msg.sender, <span class="number">7</span>)</span><br><span class="line"><span class="comment">// 重新赋值</span></span><br><span class="line">c.balance = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">delete</span> b;<span class="comment">//重置 b 的所有值为 0,除了 mapping 类型.</span></span><br></pre></td></tr></table></figure></li></ol></li><li><p>映射/字典 定义方式为 mapping, key 值最好是基础类型.Solidity 没有提供其迭代的方法.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mapping(<span class="function"><span class="params">string</span> =></span> uint) public balances; <span class="comment">// public 会自动创建一个 getter 函数.</span></span><br><span class="line">balances[<span class="string">'charles'</span>] = <span class="number">1</span>;</span><br><span class="line">balances[<span class="string">'ada'</span>]; <span class="comment">// 没有设置 key 的返回 0</span></span><br><span class="line"><span class="keyword">delete</span> balances[<span class="string">"John"</span>]; <span class="comment">// delete 不会删除元素,只会重置其初始值.</span></span><br></pre></td></tr></table></figure><p> delete 用来初始化类型的值,对映射无效.</p></li><li><p>枚举可用来创建一定数量的”常量值”够成的自定义数据类型,可以显式转为整型,但不能隐式转换.一般当做状态机使用.长度不能超过 256 位.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义状态机</span></span><br><span class="line"> enum State {Created, Locked,Inactive};</span><br><span class="line"> <span class="comment">// 声明 state 变量</span></span><br><span class="line"> State public state;</span><br><span class="line"> <span class="comment">// 赋值</span></span><br><span class="line"> state = State.Created;</span><br><span class="line"> <span class="comment">// 显式转换</span></span><br><span class="line"> uint createdState = uint(State.Created);</span><br></pre></td></tr></table></figure></li><li><p>类型转换和类型推断<br> 隐式转换: int和 int,uint 和 uint 可以相互转换,但 int 和 uint 不能转换,整数类型可以转换为 bytes,但反过来不行,任何可以转换为 uint160 的变量都可以转换为 address 地址类型.<br> 显示转换: uint8(a)<br> 类型推断: var 会在第一次赋值时推断变量类型,不可以用于函数参数,使用时小心,有时候会推断出错误类型.</p></li></ol><h3 id="单位和全局变量"><a href="#单位和全局变量" class="headerlink" title="单位和全局变量"></a>单位和全局变量</h3><ol><li><p>货币单位<br> wei, gwei, ether,默认后缀是 wei. 1ether = 10 的 18 次方 wei.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span> ether = <span class="number">10</span> ** <span class="number">18</span> wei;</span><br></pre></td></tr></table></figure></li><li><p>时间单位<br> seconds,minutes,hours,days,weeks 都可作为后缀,默认以 seconds 为单位.(years 因为闰年的原因已去除).<br> 这些单位不能直接用在变量后面,要用变量 乘 1seconds/其他单位 来使用.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uint a = <span class="number">1</span> * <span class="number">1</span> days; <span class="comment">// 值为 86400 秒</span></span><br></pre></td></tr></table></figure></li><li><p>全局变量<br>solidity 提供的通用函数或变量.</p></li></ol><ul><li>block 区块信息</li><li>msg 消息信息</li><li>tx 交易信息</li><li>abi 编码及解码函数</li><li>错误处理 throw 抛出异常,require检查由输入和外部引起的错误,assert检查内部错误,revert终止运行回撤状态并提供一个解释性字符串</li><li>数学密码学函数 addmod,mulmod,keccak256,sha256,repemd160,ecrecover</li><li>address 地址成员,包含 balance 余额,code 代码,transfer,send,call,delegatecall,staticcall</li><li>合约相关: this 表示当前合约,selfdestruct 销毁合约.</li><li>类型信息: type(x) 检索类型信息.属性包含 name,runtimeCode 等等…</li></ul><h3 id="表达式和控制语句"><a href="#表达式和控制语句" class="headerlink" title="表达式和控制语句"></a>表达式和控制语句</h3><p>Solidity 支持 js 中大部分语句 if,else,while,do,for,break,continue,return, 三元表达式,不支持 switch 和 goto 语句.tryCatch语句只能用于外部函数调用和合约创建调用.<br>Solidity 没有 js 中的非 boolean 类型自动转换的特性.<br>使用循环时注意 gas 的数量,防止合约失败.在合约中优先使用循环而不是递归,EVM 的最大调用栈的深度是 1024.<br>solidity 内部允许使用元祖(tuple)类型.<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">g</span>(<span class="params"></span>) <span class="title">public</span> </span>{</span><br><span class="line"> <span class="comment">//基于返回的元组来声明变量并赋值</span></span><br><span class="line"> (uint x, bool b, uint y) = f();</span><br><span class="line"> <span class="comment">//交换两个值的通用窍门——但不适用于非值类型的存储 (storage) 变量。</span></span><br><span class="line"> (x, y) = (y, x);</span><br><span class="line"> <span class="comment">//元组的末尾元素可以省略(这也适用于变量声明)。</span></span><br><span class="line"> (index,,) = f(); <span class="comment">// 设置 index 为 7</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>solidity 作用域规则可以参考 javascript.</p><h3 id="合约"><a href="#合约" class="headerlink" title="合约"></a>合约</h3><pre><code>合约类似于编程语言中的类,可以通过 new 关键字来创建一个新合约,在合约可以调用另一个合约的方法.调用另一个合约时会很自信一个 EVM 函数调用,这会切换执行时的上下文,这样前一个合约的状态变量就不能访问了.</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">contract infoFeed{</span><br><span class="line">...</span><br><span class="line">}</span><br><span class="line">contract Consumer{</span><br><span class="line">InfoFeed feed; <span class="comment">// 指向一个已经部署的合约;</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setFeed</span>(<span class="params">address addr</span>)</span>{</span><br><span class="line"><span class="comment">// 传入部署合约的区块链地址</span></span><br><span class="line">feed = Infofeed(addr) <span class="comment">// 显示进行类型转换,不会调用构造函数</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 创建合约实例</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createNewFeed</span>(<span class="params"></span>)</span>{</span><br><span class="line">feed = <span class="keyword">new</span> InfoFeed() <span class="comment">// 调用构造函数</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>编译器自动为所有 public 状态的变量创建 getter 函数.外部访问时被认作一个函数.<br>状态变量声明为 constant (常量)或者 immutable (不可变量)<br>合约之外的函数(也称为“自由函数”)始终具有隐式的 internal 可见性。 它们的代码包含在所有调用它们合约中,类似于内部库函数。</p><h3 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h3><pre><code>函数也是一种值类型,可以将函数传递给另外一个函数作为参数,可以再函数中返回一个函数.</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义 ,可以由多个返回值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">increment</span>(<span class="params">uint x</span>) <span class="title">returns</span> (<span class="params">uint a, uint b</span>) </span>{</span><br><span class="line">a = x+<span class="number">1</span>;</span><br><span class="line">b = a*<span class="number">2</span>;</span><br><span class="line"><span class="keyword">return</span> (a,b);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><pre><code>函数类型分为两类: 内部 internal 函数类型和外部 external 函数类型.如果函数不需要返回,可以省略 returns xx.一个函数默认是内部函数.函数有四种可见性,public(公开),private(私有,定义的合约内部访问),external(不能再合约内部调用),internal(只能从内部访问).在public函数中,solidity会立刻把函数数组参数拷贝到内存中,而external函数可以直接从calldata中读取数据。内存分配是昂贵的,直接从calldata中读取是便宜的.constructor 是构造函数,在创建合约时执行,并在内部初始化 代码 和状态变量.构造函数运行后将合约最终代码部署到区块链.View 视图函数: 减函数声明为 view 类型,这种情况下要保证不修改状态(包括修改状态,产生事件,创建合约,发送 eth,调用任何没有标记 view 和 pure 的函数,销毁合约等). Constant 之前是 view 的别名,0.5.0 移除.Pure 纯函数: 承诺不读取也不修改状态.访问 address 和 block 等其他信息都属于读取状态.纯函数能适应 revert()和 require()在发生错误是还原状态.一个合约最多有一个接收函数 receive(),声明为`receive() external payable {...}`不需要 function 关键字,也没有参数和返回值,必须用 external 和 payable 修饰.如果它不存在就会调用有 payable 的 fallback 回退函数.如果两个都没有就会在交易时抛出异常.函数修饰符: modifier(修改器) 用于在函数执行前检查某种前置条件是否满足</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">modifier onlyOwner{</span><br><span class="line"><span class="built_in">require</span>(msg.sender == owner); <span class="comment">// 判断调用合约的是不是合约所有者</span></span><br><span class="line">_;<span class="comment">// 下划线表示私有修改符的函数的方法体的替换位置</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><pre><code>回退函数 fallback: 每个合约最多只有一个,这个函数无参数也无返回值.一般有两种情况对调用回退函数,一是调用合约时没有匹配到任何函数,二是给合约发送 eth 时,交易中没有附带任何其他数据,也会调用回退函数.新版本不再推荐,推荐使用 receive 函数.</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">contract Test{</span><br><span class="line">fallback() external payable{</span><br><span class="line"><span class="keyword">throw</span>; <span class="comment">// 执行失败返回 eth 给发送者. payable 修饰符用来接收 eth</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><pre><code>自毁函数: selfdestruct(address)用来摧毁合约并将 eth 转移到给定地址.当你发现合约有问题不想让其他人使用时就可以摧毁这个合约了.摧毁之后再有人发送eth 到这个地址就会消失.solidity 支持函数重载和函数重写 overriding.父合约标记为 virtual 函数可以再继承合约里重写.重写的函数要用 override 修饰.继承: solidity 合约可以通过 is 关键字实现从父合约中继承.</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">contract A{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">contract B is A{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 多重继承</span></span><br><span class="line">contract C is A,B{</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><pre><code>接口:接口 interface 是 solidity 在版本 0.4.11 版本后引入的,接口所有函数都是抽象函数, 关键字 abstract定义抽象函数.合约中有的函数没有函数体只有函数定义的是抽象合约.库:库是一中不同类型的合约,没有存储,不拥有 eth.库中的代码可以被其他合约调用而不需要重新部署,这样可以节省大量 gas.库中没有可支付的函数(payable),没有 fallback 回退函数,库的调用通过 DELEGATECALL(委托调用,除此之外好友 call,staticcall都是低级的函数,破坏了 solidity 的类型安全性,谨慎使用) 实现,不切换上下文.Using for:using for 的声明方式是 using lib for a,意为库 lib 中所有函数默认接收 a 实例作为第一个参数.`using Balances for *`引入库 Balances 中的函数被附加在任意的类型上。</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">library C{</span><br><span class="line">funtion a() returns (address){</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">contract A{</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) <span class="title">returns</span>(<span class="params">address</span>)</span>{</span><br><span class="line"><span class="keyword">return</span> C.a(); <span class="comment">// 返回 A 合约的地址</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="事件"><a href="#事件" class="headerlink" title="事件"></a>事件</h3><pre><code>真实环境中我们需要发送交易(Transaction)来调用智能合约,我们无法立即获得合约的返回值,此时调用返回值只是该交易的 txid 或 tx hash 值.当事件真正发生时,合约将事件写入区块链时,前端才能进行响应.</code></pre><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义事件</span></span><br><span class="line">event Sent(address,indexed <span class="keyword">from</span>...);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 触发事件</span></span><br><span class="line">emit Sent(address,...)</span><br><span class="line"></span><br><span class="line"><span class="comment">// js 调用事件</span></span><br><span class="line"><span class="keyword">var</span> ClientReceipt = web3.eth.contract(xx);</span><br><span class="line"><span class="keyword">var</span> event = ClientReceipt.Sent();</span><br><span class="line">event.watch(<span class="function"><span class="keyword">function</span>(<span class="params">error,result</span>)</span>{...})</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="solidity-区块链编程入门"><a href="#solidity-区块链编程入门" class="headerlink" title="solidity 区块链编程入门"></a>solidity 区块链编程入门</h1><h3 id="编译器"><a h
</summary>
<category term="solidity ETH" scheme="http://yoursite.com/categories/solidity-ETH/"/>
<category term="solidity ETH" scheme="http://yoursite.com/tags/solidity-ETH/"/>
</entry>
<entry>
<title>认识以太坊</title>
<link href="http://yoursite.com/2019/09/18/%E8%AE%A4%E8%AF%86%E4%BB%A5%E5%A4%AA%E5%9D%8A/"/>
<id>http://yoursite.com/2019/09/18/认识以太坊/</id>
<published>2019-09-18T11:29:39.000Z</published>
<updated>2021-08-30T11:30:52.053Z</updated>
<content type="html"><![CDATA[<h1 id="认识以太坊"><a href="#认识以太坊" class="headerlink" title="认识以太坊"></a>认识以太坊</h1><ol><li>以太坊不同于比特币,它是一种图灵完备的系统.以太坊能够执行存储在区块链之上的程序的能力,是由被称为EVM的状态机完成的,创建了一个分布式的单体状态世界计算机。</li><li>根据图灵的理论,在真正运行合约之前,以太坊实际上无法预先判断一个合约是否会运行终止,或者它需要运行多久,也许这个合约会陷入死循环一直运行。无论是程序中的瑕疵,还是故意为之,智能合约都可能在一个节点试图验证它的时候永远不停地执行下去,这也就造成了一种DDoS攻击的后果。为了应对这个挑战,以太坊引入了名为gas的计量机制。以太坊的货币是 eth,不同于比特币,eth 的总量没有上限,它的最小单位是 wei,1eth 等于 10 的 18 次方 wei.</li><li>DAPP 去中心化应用,代表更为广泛的智能合约.它需要一个智能合约和web 用户界面,是一个区中心化的 web 应用程序.</li><li>Web3 是一种去中心化的互联网协议.</li><li>选择一款以太坊钱包(推荐MetaMask可以运行在 chrome 中),并保管好你的私钥.在交易时矿工会收取一部分手续费,手续费是由矿工决定的.交易完成后可以通过以太坊区块浏览器 查询到.</li><li>钱包创建的账户是外部账户,用户掌握私钥.而合约账户掌握智能合约代码,没有私钥,它由代码所控制.合约具有地址,可以接收和发送以太币.合约账户无法启动交易,但合约可以互相调用,对交易做出反应.</li><li>以太坊支持多种编程语言,最常用的是Solidity,有以太坊创始人之一Gavin Wood 创立.Solidity 编译器会把代码编译成EVM 字节码然后可以再区块链上的 EVM 中所执行.在次之前需要给合约注册到以太坊链上,需要通过一个特殊的交易,这个交易的目标地址是0x0000000000000000000000000000000000000000,被称为零地址,用来告诉以太坊区块链用户希望通过这样的交易来注册合约.注册成功后合约有了自己的地址.</li><li>有人向合约地址发送交易就会触发合约在 EVM 上的执行,交易的本身就是合约函数的输入参数.合约发起的交易称内部转账或消息.</li><li>请注意,JavaScript的限制,大于10的17次幂 的数值无法处理,我们需要单独处理.</li><li>以太坊协议分别有六种实现,分别是以 rust,pathon,Go,Java,cpp,scala 实现的.开发以太坊并不需要在主网上有运行一个全功能节点,可以再测试网络上的节点完成开发.测试网络节点连接的是一个公共测试区块链.</li><li>以太坊客户端的API是一组远程过程调用(RPC)命令,并采用JSON格式编码。这被称为JSON-RPCAPI。最基本的,JSON-RPCAPI作为一个接口,允许程序员编写代码,把以太坊客户端作为进入以太坊网络和区块数据的大门-网关.RPC接口使用8545端口以HTTP协议的方式对外服务。出于安全的目的,在默认情况下对这个端口的访问是受限的,只允许来自本地网络的链接.RPC 接口提供了很多服务,比如“管理钱包中的私钥和以太坊地址。创建、签名并广播交易。通过交易内数据载荷的方式与智能合约交互。浏览并使用DApp。提供外部服务的链接,例如区块浏览器。转换以太币的单位,向浏览器注入一个 web3 实例.移动钱包都属于远程客户端.</li><li>不要尝试用编程的方式 创建随机数,使用密码学安全的伪随机数生成器(如CSPRNG),并且使用熵源充足的随机源做种子,可以确保它在密码学上是安全的.</li><li>椭圆曲线程序库, openSSL和libsecp256k1(bitcoinCore).</li><li>加密哈希函数是一种单向的哈希函数,可以将任意长度的数据映射为固定长度的比特序列。这种“单向性”意味着,如果我们只知道哈希函数的输出值,还原出输入数据在计算上是不可能的。以太坊使用的哈希算法是Keccak-256(爱德华斯诺登爆出美国国家安全部门在该算法的随机数生成器中留有后门), 可能会更改为FIPS-202 SHA-3. 不同于比特币,以太坊的地址没有校验信息,在交易时注意地址是否正确.EIP55 提案增加了校验,但只有支持 EIP55 的钱包才能使用.</li><li>以太坊的交易信息中有一个 nonce值,它是一个记数器,如果同时发起多笔交易,矿工是以任意顺序接收交易的,交易信息中有 nonce 的比没有 nonce 值的交易优先级要高.如果你按顺序创建了一系列交易,但其中一个没有得到确认,那么之后的所有交易都会“堵”住,等待这个缺失的交易。如果某个 nonce 的值不对或者没有足够的 gas,交易都会被堵住.</li><li>交易信息中还包含 gas 和 gasLimit 这两个信息,gas 不是以太币,但它跟以太币有汇率关系会上下波动.gas 是用来支付给矿工的手续费,高额的 gas 可以让你的交易快速完成.gasLimit 是付款人为了完成交易愿意付出的最大 gas 量.由于每次调用合约,合约的计算量会根据代码决定,为了应对突发状况,你需要多付出一些 gas.以太坊没有找零机制.</li><li>向一个错误的地址 发送交易会导致 eth 被销毁(进入黑洞),因为错误的地址没有私钥,无法完成签名去使用它.</li><li>交易的核心数据是 value 和 data 两个字段,他们都可以为空.接收者为外部账户,不管对方是否存在该金额都会转移到接收地址.如果接收者是合约地址,就会执行 EVM 合约,data为空时执行合约的回退函数.data 有内容时,data字段的内容会被EVM解读为针对合约的函数调用,调用data中指定的函数,并把需要的参数传递给这个函数。</li><li>一但被部署之后,智能合约就不能被更改,更改的唯一方式就是部署一个新的合约实例.但合约实例可以被删除,当把合约地址和内部存储信息清空之后合约就成了一个空地址,系统会提供 gas 退款,用来激励大家释放资源.需要注意,只有合约的开发者在代码中编写了对应的删除功能,SELFDESTRUCT字节码才会起作用。否则无法删除合约实例.</li><li>智能合约为程序员设定了一个很高的门槛:如果有bug,可能会损失大量的金钱。因此,编写智能合约就需要极力避免任何可能的副作用。</li><li>gas的费用由交易的发起方支付,因此我们需要避免调用那些可能引发高额gas的合约或者函数。程序员的最佳策略就是设法避免合约可能产生的 gas 消耗.比如避免对动态数组所执行的循环操作,避免调用未知的合约.</li><li>Vyper是在以太坊虚拟机上执行的、面向合约的实验性编程语言,旨在提供更好的可审计性,并帮助程序员更轻松地完成清晰易懂的代码.OpenZeppelin项目完成了一项伟大的工作,它为以太坊社区构建并审计了很多安全的库合约,可以避免漏洞.</li><li>有很多可重用的代码可以供我们编写自己的合约,包括已经部署到链上的可调用合约以及可以作为代码模板使用的库合约代码.有OpenZeppelin 和 zeppelinOS 等</li><li>ERC20 标准是在以太坊上构建代币的标准.工具型代币是指用来支付某个服务、应用或资源的代币,比如 gas。权益型代币可以被设定为没有投票权的,用来分红和分配利润的代币,也可以承载一个去中心化自治组织的投票权,这类组织的管理由持有代币的所有人共同投票决定.</li><li>ERC 合约必须实现如下函数和事件,总发行量 totalSupply,balanceOf 余额,transfer 转账,transform 转账信息,approve 审核,allowance配额,Transfer 转账事件,Approval 审核事件,name 名称,其他的都是可选函数.symbol 符号等.</li><li>ERC20支持两种转账。第一种是单一式转账,直接使用transfer函数。这个流程被钱包软件用来发送代币给其他地址。大多数的代币转账都是通过transfer函数完成的。第二种方式是使用了approve和transferFrom的两步式交易。这个流程允许代币的持有人授权其他地址操纵他们的代币。这通常用于授权给某一个合约地址,进行代币的分发,但也可以用于交易所的场景。</li><li>以太币是使用带有目标接受人地址的交易进行转账的,而代币的转账是通过代币合约内相关的状态转换进行的,使用合约作为地址,而不是接收方的地址。代币合约跟踪余额并发出事件。在代币的转账过程中,并没有任何针对接收方地址的交易。接收方的地址被加入代币合约的余额映射表中。即使是支持ERC20代币的钱包软件,也不会自动发现对应地址上的代币余额变化,触发用户主动把某个代币合约添加到钱包的关注列表。大多数ERC20代币都像是垃圾邮件一般毫无价值。</li><li>以太币使用send函数发送,任何合约中的可支付函数或者外部账户都可以接收以太币。代币使用transfer或approve&transferFrom函数发送,这些函数只存在于创建这个代币的合约中.并不会触发接收方合约中的任何可支付函数。</li><li>预言机(oracle),它是可以为以太坊智能合约提供外部数据源的系统。为了保持共识,EVM的执行过程必须完全确定,并且仅基于以太坊状态和签名交易的共享上下文。这产生了两个特别重要的后果:一个是EVM和智能合约没有内在的随机性来源;另一个是外部数据只能作为交易的数据载荷引入。我们可以引入外部信息,包括随机性、价格信息、天气预报等,作为发送到网络的交易的数据部分。但是,这些数据本身无法信任,因为它的来源无法核实。我们使用预言机尝试解决这个问题.理想状态下预言机可以无信任的获取外部信息,用于智能合约,还可以把信息安全的中继到 dapp 前端.</li><li>预言机的设计模式: 所有的预言机都提供了一些关键功能。这些能力包括:从链外的数据源收集数据。使用签名消息在链上传输数据。将数据放入智能合约的存储空间,使数据可用。其他智能合约再调用预言机智能合约的检索功能来访问它.预言机的三种主要方式可以分为请求与响应、发布与订阅和立即读取。数据证明:让链外方法可以证明返回数据的完整性是非常关键的。两种常见的数据认证方法是真实性证明以及可信执行环境,上面列举出的所有机制描述的都是中心化的预言机系统,都需要依赖可信的权威。</li><li>去中心化预言机可以用于保证数据可得性,还可搭配链上数据汇总系统创建独立数据提供者网络。比如 chainLink,它分为三部分分别是声誉合约、订单匹配合约、数据汇总合约.</li><li>DAPP 通常是指具有 web 前端的智能合约.Dapp 的后端部署在区块链上,无法篡改.因为在智能合约中执行计算非常昂贵,所以应该让它尽量小.当需要计算量特别大时,一般选做外部计算或使用外部数据源.但前提是你的用户必须信任这些外部源.Dapp 的前端与传统前端并无不同,通过 web3.js 连接以太坊.</li><li>不仅是计算,储存数据也通常储存在链下.可以是中心化的数据存储平台,也可以说去中心化数据存储平台IPFS和以太坊自有平台 Swarm.星际文件系统(IPFS)是一个去中心化的、内容可寻址的存储系统,它可以在P2P网络中分配存储的对象。Swarm是另外一个内容可寻址的、类似于IPFS的P2P存储系统。</li><li>任何应用都会包含的重要组件是进程之间的通信。传统情况下,这可以通过依赖中心化的服务器进行。但是,也有许多基于P2P网络的替代方案。对DApp来说,最值得关注的P2P消息协议是Whisper.</li><li>一些DApp可能设置一个或者多个具有特殊功能的特权账户,如终止DApp合约,覆盖或者更改合约配置,甚至“否决”某些操作。通常,在DApp中引入这些治理功能是为了避免某些未知的问题而引起的bug。在构建一个DApp时,你必须决定是让其完全独立,一旦部署之后就无法被控制,还是创建一个特权账户,承受受到攻击的风险。任何一种选择都会带来风险,但是长远来看,真正的DApp不应该存在能进行特权访问的特权账户,因为这不是去中心化。</li><li>以太坊名称服务:ENS不仅仅是一个智能合约,它本身是一个基础的DApp,提供去中心化的名称服务。是以太坊的域名(DNS服务商 ,域名是.eth,它通过一个拍卖系统被分发,并且没有保留列表或者优先级,获得名称的唯一办法就是使用该系统。ENS的顶层是另一个超级简单的合约,目的只有一个:持有资金。如果所有者不再需要这个名称,他们可以将其卖给系统并拿回他们的以太币(资金只能转给所有者).这种方法大大降低了因为bug引起的攻击而给资金带来的风险。</li><li>以太坊虚拟机 EVM 用来处理智能合约的部署和执行.EVM有一个基于栈的架构,在一个栈中保存了所有内存数值 。EVM的数据处理单位被定义为256位的“字”(这主要是为了方便处理哈希运算和椭圆曲线运算操作),并且它还具有以下数据组件:一个不可变的程序代码存储区ROM,加载了要执行的智能合约字节码。一个内容可变的内存,它被严格地初始化为全0数值。一个永久的存储,它是作为以太坊状态的一部分存在的,也会被初始化为全0.它仅仅是一个计算引擎,仅提供对计算和存储的抽象,就像Java虚拟机(JVM)那样。从高级视角来看,JVM的设计提供了一个无须知晓底层宿主OS或硬件的运行环境,从而提供了跨不同系统平台的兼容性.EVM 的执行顺序由以太坊发起的交易决定,就像是 js 中的单线程.</li><li>EVM既没有任何“系统接口”,也没有“硬件支持”,因为并没有任何物理机器需要与之交互。以太坊世界计算机是完全虚拟化的。EVM 的任务是基于以太坊协议、根据智能合约代码的执行来计算合法的状态转换,用以更新以太坊的状态。</li><li>由于停机问题,以太坊世界计算机就有了一个被程序要求永远执行下去的风险。这可能是由于某些意外情况或者恶意的目的。不过在有了gas之后,也就有了一个解决方案:如果在一个预先指定的最大计算量被用尽的时候计算还没有结束,那么所有处理都会无条件地停止。这个限制并不是固定的,你可以通过支付费用来将它提高到最大值.</li><li>与比特币协议中仅仅以交易数据的字节大小来计算交易费不同,以太坊协议中计算交易费Gas时需要计量由交易和智能合约代码执行所引发的所有计算步骤。比如相加两个数值需要消耗3 gas,发送一个交易需要消耗21000 gas.Gas 是以太币和矿工奖励之间的缓冲,增加攻击者的攻击成本.如果智能合约执行完成,gas 作为交易费给矿工,矿工费= 合约实际消费的gas * gas 的价格(与以太币的汇率)最后得到一笔以太币,交易剩余的 gas 兑换成以太币返还. 交易未成功的话也需要支付交易费,因为矿工为发生错误的操作执行了计算.</li><li>在一个敌意环伺、没有中心化控制者的分布式网络中达成共识的能力是所有开放性公有区块链的核心。比如工作量证明POW是支撑比特币的最重要的发明,奖励只是工具,它的目的是保护比特币系统的安全和去中心化.PoW共识意味着一套风险与收益的细致权衡,驱动着参与者出于自利而遵循共识规则行动。</li><li>POW 提出之前就已经出现了权益证明共识 POS.以太坊也正在由 pow 逐渐向 pos 过渡.PoS算法的运作流程大体如下: 持有以太币的人会有投票权,但是需要质押自己的资产充作保证金,投票的权重和保证金成正比,投票的区块通过则按比例获取奖励,不通过则失去保证金.另外还有一种 DPOS 它是分布式的 POS.</li><li>POW 的币种都要考虑有 ASIC 矿机造成的算力集中,GPU短缺,电力浪费等问题.</li><li>THE DAO 去中心化的自治组织,在ICO众筹自己发行的代币 DAO 时遭到黑客攻击,损失了 360 万 ETH. 以太坊社区针对是否回滚发生争议,硬分叉为 ETC(不回滚) 和 ETH (回滚)两条链.根据社区投票,ETH 为客户端默认选择.</li><li>Truffle框架由若干个Node.js包构成。用来开发以太坊智能合约.相似的框架还有 embark和 OpenZeppelin,zeppelinOS.web3 提供了与以太坊连接的接口.Ganache 提供了一个本地测试区块链用来测试智能合约.</li><li>以太坊共包含流程,从下往上分别是 数据层(区块和区块链),网络层(p2p),共识层(pow/pos),激励层(挖矿和 gas),合约层(EVM 和智能合约),应用层(DAPP,钱包)</li></ol>]]></content>
<summary type="html">
<h1 id="认识以太坊"><a href="#认识以太坊" class="headerlink" title="认识以太坊"></a>认识以太坊</h1><ol>
<li>以太坊不同于比特币,它是一种图灵完备的系统.以太坊能够执行存储在区块链之上的程序的能力,是由被称为EVM
</summary>
<category term="ETH" scheme="http://yoursite.com/categories/ETH/"/>
<category term="ETH" scheme="http://yoursite.com/tags/ETH/"/>
</entry>
<entry>
<title>认识比特币</title>
<link href="http://yoursite.com/2019/08/30/%E8%AE%A4%E8%AF%86%E6%AF%94%E7%89%B9%E5%B8%81/"/>
<id>http://yoursite.com/2019/08/30/认识比特币/</id>
<published>2019-08-30T11:27:11.000Z</published>
<updated>2021-08-30T11:29:18.377Z</updated>
<content type="html"><![CDATA[<h1 id="认识比特币-BTC"><a href="#认识比特币-BTC" class="headerlink" title="认识比特币(BTC)"></a>认识比特币(BTC)</h1><ol><li>比特币地址以 1 和 3 开头. 1 开头的地址,比特币只能通过私钥签名和公钥哈希后才能消费,3 开头的是P2SH 地址,代表多重签名.</li><li>钱包是多个地址和对应密钥的集合.</li><li>没有交易过的地址是无效地址,交易过的地址就已经在全网公布了,可以再全账本或区块链浏览器中查到交易记录.交易后该记录是未确认状态,由矿工打包到区块中并链接到链上后变为确认状态.</li><li>在交易时,钱包会从多个地址找到有余额的地址(UTXO 意为未花费的交易输出),然后组合成可消费当前金额的组合(不一定相等,但一定是不少于可消费金额),然后创建一个找零地址,为了让这笔交易快速确认再付一笔矿工费,再发送出去.矿工费可能是设置的找零金额和实际找零金额的差值,所以要确认这个金额是否相差过多.</li><li>ASIC矿机是一个集成可上百个挖矿专用算法硬件且能同时在一个单独芯片上并行工作的专用集成电路.而矿池是多个矿工共享算力和依靠贡献分成的.</li><li>交易发送到区块链网络时,区块链网络上的节点会把这些交易放到未验证的交易池当中.矿工构建新区块时会从交易池中放到新区块中,以矿工费为优先级进行排序,然后计算哈希开始挖矿.矿工一旦从网络上收到新区块时,会立刻放弃当前挖的区块,重新生成新区块.当生成新快成功后,区块奖励的比特币会支付到自己的比特币地址,如果是矿池会根据工作量进行分配.每生成一个区块,对于上一个区块就多了一个证明.按照惯例,6 个确认之后就被认为是不可撤销的.</li><li>比特币核心客户端是一个完整的账本,随着时间发展,账本会越来越大.按照后可以使用 bitcoin-cli 工具,包含了很多工具和功能.比如钱包设置和加密,备份,文本导出和恢复.接收交易等等</li><li>拥有比特币的秘钥就拥有该账户的控制权,秘钥是非对称加密的,公钥向外展示是比特币地址,私钥用来签名不对外公开.可以根据私钥计算出公钥,一般存储时只储存私钥.私钥是一个数字随机选出(256 位的二进制,2 的 256 次幂),然后根据椭圆加密曲线这个单向加密函数产生一个公钥.有了公钥就可以根据哈希函数生成比特币地址.</li><li>素数幂和椭圆曲线算法是不可逆的,很容易向一个方向计算但不能逆向去推算.使得数字签名称为可能.比特币使用椭圆曲线算法作为其公钥加密的基础算法.</li><li>比特币交易时会使用私钥对公钥进行签名,每次签名都不同,网络中的节点可以根据公钥和签名进行验证,确认该笔交易是否拥有比特币的所有权.</li><li>椭圆曲线算法:是一种基于离散对数问题的非对称加密算法,它是单向的函数.比特币使用了一种 secp256k1 标准所定义的一条特殊椭圆曲线和一系列数学常数.以一个随机生成的私钥 k 为起点,将其与曲线上已经定义的生成点 G相乘 获得曲线上的另外一个点 K 即公钥.生成点 G 是 secp256k1 标准的一部分,比特币的生成点都是一样的, K = k*G,公钥和私钥关系是固定的,但只能单向运算,所以暴露公钥并不会威胁到私钥的安全.(椭圆曲线之上的算术运算跟常规的数学运算是不一样的。一个点(G)可以与一个整数(k)相乘来获得另外一个点(K)。但是椭圆曲线的世界里没有除法的概念。因此不可能简单地通过计算公钥K对G点的除法来计算私钥。这就是本章前面提到的单向数学函数。</li><li>哈希加密算法在比特币中广泛运用,包括比特币地址的生成,工作量证明等等.比特币私钥 SHA256 哈希加密算法. 根据公钥进行 sha256 和RIPEMD 160 两次哈希运算的到160bit或20字节的公钥哈希,然后根据 base58check编码获取比特币地址(前缀为 0x00). 因为 256 位二进制过长,交易不便,base58 编码和 base64 不同的是, base58不仅实现了数据压缩和易读性还具有错误诊断功能.它不包含 base64 中的数字0 ,大写 o,小写 l,大写 I 这些容易混淆的字符.base58check增加了错误校验,数据中自带校验码,错误的比特币地址不会被认为有效地址从而造成损失.</li><li>早期比特币钱包只是随机生成的私钥集合,称非确定性(随机)钱包,难以管理备份和导出.这与避免地址重复使用原则相违背(每个地址最好只使用一次,可以增加安全性),后面出现的确定性钱包现在是主流,确定性钱包可以从公共的种子生成大量私钥,创建备份导出时只关注种子就可以了.助记词是英文单词序列用作种子所对应的确定性钱包的随机数私钥.单词的顺序就是钱包的备份.用来恢复钱包.助记词被定义在比特币改进协议 BIP0039 中作为草案而不是标准方案.通过 BIP0039 生成的钱包是 HD(分层确定性)钱包,由种子生成主密钥,再生成子密钥,子密钥又可以生成孙密钥.</li><li>BI0038提出一个通用标准,使用一个口令加密私钥并使用Base58Check对加密的私钥进行编码,这样加密的私钥就可以安全地保存在备份介质里,安全地在钱包间传输,保持密钥在任何可能被暴露情况下的安全性。其本质就是私钥和密码混合形成加密私钥.</li><li>比特币地址以 1 和 3 开头. 1 开头的地址,比特币只能通过私钥签名和公钥哈希后才能消费,3 开头的是P2SH 地址,代表多重签名.多重签名需要从 N 个密钥中需要 M 个私钥签名才可以消费这笔金额.</li><li>比特币开发者针对比特币客户端进行交易的脚本类型设置了一些限制,然后编译为一个叫standard 的函数,该函数定义了五种类型的交易分别是P2PKH、P2PK、MS(限15个密钥)、P2SH(多重签名)和OP_Return”</li><li>并非所有的节点都有能录存储完整的区块链账本,通过简化的支付验证(SPV)的方式可以使它们在不必存储完整区块链的情况下进行工作.SPV 节点只需要下载区块头而不用下载交易信息.一个拥有完整区块链的节点会构造一条验证链,这条链是由沿着区块链按时间倒序一直追溯到创世区块的数千区块及交易组成。而一个SPV节点会验证所有区块的链(但不是所有的交易),并且把区块链和有关交易链接起来.但它不是绝对安全的,如果要保证万无一失的安全性,最可靠的方法还是运行完整区块链的节点。</li><li>在SPV节点里,Bloom 过滤器被用来向对等节点发送交易信息查询请求,同时交易地址不会被暴露。</li><li>比特币网络中几乎每个节点都会维护一份未确认交易的临时列表,被称为内存池或交易池.内存池中的交易如果长时间未处理就会消失,交易人可以重新发起交易或提高矿工费以提高优先级.</li><li>一个区块出现多个子区块的情况被称为“区块链分叉。但这只是暂时的,最长链原则决定了分叉的问题.区块越长,篡改的成本越大,区块链越安全,这也是比特币的一个特征.</li><li>一个区块的大小是 1M,一笔交易最小是250 字节,也就是一个区块最多包含 500 笔交易.一个区块有区块头,交易额记数,交易信息三部分组成.</li><li>挖矿保护了比特币系统的安全,并且实现了在没有中心机构的情况下,也能使整个比特币网络达成共识。铸造新币的奖励和交易费是一种激励机制,它可以调节矿工行为和网络安全,同时又完成了比特币的货币发行.比特币每出 210000 个区块奖励就会减半.2140 年奖励为最小单位,停止奖励,总金额是 2100w 个.区块奖励不同于其他交易,没有付款人,只包含一个 coinbase 的输入,仅仅是用来创建新的比特币,叫创币交易.</li><li>工作量证明的难度单位为 1TH/s 表示每秒可以处理 1 万亿次哈希运算.比特币每 10 分钟出一个区块,但并不是没 10 分钟进行一个难度调整,而是 2016 个块会调整一次难度,会根据 2016 个区块的总共花费的时长与 20160 分钟进行比对,之后再决定该如何调整.</li><li>51%攻击,当一个或者一群拥有了整个系统中大量算力(一些安全模型认为 30%的算力就可发动 共识攻击)的矿工出现之后,他们就可以通过攻击比特币的共识机制来达到破坏比特币网络的安全性和可靠性的目的.共识攻击不能从其他的钱包那里偷到比特币、不签名地支付比特币、重新分配比特币、改变过去的交易或者改变比特币持有纪录。共识攻击能够造成的唯一影响是影响最近的区块(最多10个)并且通过拒绝服务来影响未来区块的生成。这也是惯例 6 个区块确认才算做安全交易的由来.</li><li>莱特币的工作量证明与比特币不同,比特币是基与 SHA256,而莱特币为了避免 ASIC 矿机导致算力过于集中,采用了scrypt 算法,便于 CPU 挖矿.</li></ol>]]></content>
<summary type="html">
<h1 id="认识比特币-BTC"><a href="#认识比特币-BTC" class="headerlink" title="认识比特币(BTC)"></a>认识比特币(BTC)</h1><ol>
<li>比特币地址以 1 和 3 开头. 1 开头的地址,比特币只能通过私钥
</summary>
<category term="BTC" scheme="http://yoursite.com/categories/BTC/"/>
<category term="BTC" scheme="http://yoursite.com/tags/BTC/"/>
</entry>
<entry>
<title>Docker使用总结</title>
<link href="http://yoursite.com/2019/07/30/Docker%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/"/>
<id>http://yoursite.com/2019/07/30/Docker使用总结/</id>
<published>2019-07-30T09:34:47.000Z</published>
<updated>2021-08-30T09:39:13.463Z</updated>
<content type="html"><![CDATA[<h3 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h3><h4 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 镜像相关</span></span><br><span class="line">docker images <span class="comment"># 查看镜像</span></span><br><span class="line">docker search xxx <span class="comment"># 搜索互联网镜像</span></span><br><span class="line">docker pull xxxx <span class="comment"># 拉取镜像</span></span><br><span class="line">docker rmi xxxx <span class="comment"># 删除镜像</span></span><br><span class="line"><span class="comment"># 容器相关</span></span><br><span class="line">docker ps <span class="comment"># 查看运行的容器 </span></span><br><span class="line">docker ps -a <span class="comment"># 查看所有容器 </span></span><br><span class="line">docker run <span class="comment"># 运行容器,-i运行容器,-t创建后进入命令行,-d后台运行,-p端口映射,-v目录映挂载,--name命名</span></span><br><span class="line"><span class="comment">#交互式 docker run -it --name=mycentos centos:7 /bin/bash</span></span><br><span class="line"><span class="comment"># 守护式 docker run -di --name=mycentos1 centos:7</span></span><br><span class="line">docker exex -it xxxxx /bin/bash <span class="comment"># 进入容器 </span></span><br><span class="line">docker stop xxx <span class="comment"># 停止容器</span></span><br><span class="line">docker start xxx <span class="comment"># 启动容器</span></span><br><span class="line"><span class="comment"># 文件拷贝</span></span><br><span class="line">docker cp 需要拷贝的目录 容器名:容器目录</span><br><span class="line">docker cp 容器名:容器目录 需要拷贝的目录</span><br><span class="line"><span class="comment"># 通过目录挂载将目录与容器目录进行映射 通过--privileged=true来解决目录权限问题</span></span><br><span class="line">docker run -di -v /xx/YY:/xx/YY --name=mycentos2 cnetos:7</span><br><span class="line"><span class="comment"># 查看容器相关数据 </span></span><br><span class="line">docker inspect xxx</span><br><span class="line"><span class="comment"># 查看容器ip</span></span><br><span class="line">docker inspect --format=<span class="string">'{{.NetworkSrtting.IPAddress}}'</span> xxx</span><br><span class="line"><span class="comment"># 删除指定容器</span></span><br><span class="line">docker rm xxx</span><br></pre></td></tr></table></figure><h4 id="应用部署"><a href="#应用部署" class="headerlink" title="应用部署"></a>应用部署</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#mysql</span></span><br><span class="line">docker pull centos/mysql-xxx</span><br><span class="line">docker run -di --name=testmysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql</span><br><span class="line">docker exex -it testmysql /bin/bash</span><br><span class="line">mysql -uroot -pxxx</span><br><span class="line"></span><br><span class="line"><span class="comment">#nginx</span></span><br><span class="line">docker pull nginx</span><br><span class="line">docker run --name nginx-test -p 8080:80 -d nginx</span><br><span class="line">docker <span class="built_in">exec</span> -it nginx-test /bin/bash</span><br><span class="line"></span><br><span class="line"><span class="comment">#tomcat</span></span><br><span class="line">docker pull tomcat</span><br><span class="line">docker run --name myTomcat -p 8080:8080 -v <span class="variable">$PWD</span>/xxx:/usr/<span class="built_in">local</span>/tomcat/webapps/ -d tomcat <span class="comment">#目录映射</span></span><br><span class="line">docker <span class="built_in">exec</span> -it myTomcat /bin/bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># mongo</span></span><br><span class="line">docker pull mongo</span><br><span class="line">docker run -itd --name myMongo -p 27017:27017 mongo --auth</span><br><span class="line">docker <span class="built_in">exec</span> -it myMongo mongo admin</span><br><span class="line"><span class="comment"># 创建一个名为 admin,密码为 admin 的用户。</span></span><br><span class="line">> db.createUser({ user:<span class="string">'admin'</span>,<span class="built_in">pwd</span>:<span class="string">'admin'</span>,roles:[ { role:<span class="string">'userAdminAnyDatabase'</span>, db: <span class="string">'admin'</span>}]})</span><br><span class="line"><span class="comment"># 尝试使用上面创建的用户信息进行连接。</span></span><br><span class="line">> db.auth(<span class="string">'admin'</span>, <span class="string">'admin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># RabbitMQ</span></span><br><span class="line">docker search rabbitmq</span><br><span class="line">docker pull rabbitmq:3.8.3-management</span><br><span class="line">docker run -di --name myRabbitmq -p 5671:5617 -p 5672:5672 -p 4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 rabbitmq:management</span><br><span class="line"><span class="comment"># 访问 http://127.0.0.1:15672/</span></span><br></pre></td></tr></table></figure><h4 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#容器保存为镜像</span></span><br><span class="line">docker commit 容器名称xxxx 镜像名称YYY</span><br><span class="line"><span class="comment"># 镜像备份</span></span><br><span class="line">docker save -o xxx.tar 镜像名称YYY</span><br><span class="line"><span class="comment"># 镜像恢复与迁徙</span></span><br><span class="line">docker load -i xxx.tar</span><br></pre></td></tr></table></figure><h4 id="DockerFile"><a href="#DockerFile" class="headerlink" title="DockerFile"></a>DockerFile</h4><p>dockerfile是有一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。</p><p>dockerFile常用命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">FROM image_name:tag 定义使用的基础镜像</span><br><span class="line">MAINTAINER user_name 什么镜像创建者</span><br><span class="line">ENV key calue 设置环境变量</span><br><span class="line">RUN <span class="built_in">command</span> 命令执行</span><br><span class="line">ADD 原目录/文件 目标目录/文件 将文件移动到容器</span><br><span class="line">COPY 原目录/文件 目标目录/文件 将文件复制到容器</span><br><span class="line">WORKDIR 目录 设置工作目录</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h3 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h3><h4 id="常用命令"><a href="#常用命令" class="headerlink" title="常
</summary>
<category term="后端 docker" scheme="http://yoursite.com/categories/%E5%90%8E%E7%AB%AF-docker/"/>
<category term="docker" scheme="http://yoursite.com/tags/docker/"/>
</entry>
<entry>
<title>vim入门指南</title>
<link href="http://yoursite.com/2019/05/09/vim%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97/"/>
<id>http://yoursite.com/2019/05/09/vim入门指南/</id>
<published>2019-05-09T11:24:13.000Z</published>
<updated>2021-08-30T11:40:09.864Z</updated>
<content type="html"><![CDATA[<h1 id="vim入门指南"><a href="#vim入门指南" class="headerlink" title="vim入门指南"></a>vim入门指南</h1><h3 id="快捷操作"><a href="#快捷操作" class="headerlink" title="快捷操作"></a>快捷操作</h3><pre><code>- di" 删除本行""内的文本- cw 替换一个词- yaw 复制当前词- f和 t来查找当前行字符,`;`重复查找,`*`查找当前词,n 跳往下一个.- ctrl + d/u 向下/上 滚动半屏- 分隔符 a/i 带空格/不带空格 ,例如 a(/i( ,一般 d 和 a 配合,c 和 i 配合, w/词 -> s/句子->p/段落.- u 撤销修改,U 撤销所有修改(undo), 撤销最后一次修改 ctrl + R- i/I,a/A,o/O 插入- mm 命令可以标记一个位置,然后~m回到标记点- 全局替换`:%s/{pattern}/{string}/{flags}`- 宏: q a-z 命名, 操作 q 离开完成.调用 @ a-z 具体的宏.</code></pre><h4 id="字符跳动"><a href="#字符跳动" class="headerlink" title="字符跳动"></a>字符跳动</h4><ul><li>h,j,k,l 左下上右</li></ul><h4 id="模式"><a href="#模式" class="headerlink" title="模式"></a>模式</h4><p>所有命令都需要在normal模式下使用</p><ul><li>i切换到insert模式,按esc回到normal模式</li><li>v 可视化模式,按v或V进入可视模式<br>v可以进入面向字符的可视模式,V 是针对行的可视模式.ctrl +v可以进入针对列的可视模式.可视模式下按 o 可以调整选择区边界.</li><li>:wqa! w为保存,q为离开,!为强制执行 ,a表示all所有</li></ul><h4 id="行内移动"><a href="#行内移动" class="headerlink" title="行内移动"></a>行内移动</h4><ul><li>0 到行头</li><li>^ 第一个不是空的位置</li><li>$ 行尾</li><li><p>g_ 最后一个不是空的位置</p></li><li><p>w 下一个单词开头</p></li><li>b 当前或上一个单词开头</li><li><p>wi/bi 在开头前插入</p></li><li><p>e 当前或下一个单词结尾 (end)</p></li><li>ge 上一个单词结尾</li><li><p>ea,gea 在结尾后插入</p></li><li><p>fa 下一个字符为a的位置</p></li><li>ta 字符a前的第一个字符</li></ul><h4 id="页移动"><a href="#页移动" class="headerlink" title="页移动"></a>页移动</h4><ul><li>H 跳转到当前屏幕第一行 (head)</li><li>M 当前屏幕中间一行 (middle)</li><li><p>L 最后一行 (last)</p></li><li><p>2H 移动到当前屏幕第2行</p></li><li>3L 移动到当前屏幕倒数第三行</li><li>ctrl + f 前移一页 (forward)</li><li>ctrl + b 后移一页 (back)</li><li>ctrl + d 往下滚动半屏 (down)</li><li><p>ctrl + u 往上滚动半屏 </p></li><li><p>gg 调到首行 相当于:1</p></li><li>G 调到尾行</li><li>nG 调到n行 相当于:n</li><li>% 调到另一边括号 ({[]})</li></ul><h4 id="配合查找字符方式移动"><a href="#配合查找字符方式移动" class="headerlink" title="配合查找字符方式移动"></a>配合查找字符方式移动</h4><ul><li>fa 正向移动到第一个字符a处, ta 正向移动到下一个a字符之前</li><li>Fa 逆向移动到第一个字符a处, Ta 逆向移动到下一个a字符之前</li><li><code>;</code>可以重复查找上次 f 命令查找的字符,不用再重复使用 f 命令.如果按了太多次,可以按<code>,</code>回到上次位置.</li><li><code>*</code>命令可以查找光标当前所在词,按 n 跳往下一个.</li><li>f/t 只能在行内查找且查找时只能查找一个字符,而不是单词.查找时尽量选择频率比较低的字符从而快速移动到目标处.</li></ul><h4 id="非相邻单词或字符间移动"><a href="#非相邻单词或字符间移动" class="headerlink" title="非相邻单词或字符间移动"></a>非相邻单词或字符间移动</h4><ul><li>8w 正向移动到相隔8个单词的首字符</li><li>4Fa 逆向移动到第4个a字符</li></ul><h4 id="搜索匹配"><a href="#搜索匹配" class="headerlink" title="搜索匹配"></a>搜索匹配</h4><ul><li>/text 向后搜索 </li><li>? text 向前搜索</li><li>:g/word 全局搜索</li><li>n 搜索下一个同样的内容</li><li><p>N 搜索上一个同样的内容</p></li><li><p>查找 :{作用范围}s/{目标}/{替换}/{替换标志}</p></li><li><p>:%s/old/new/gc 全局替换</p></li><li><p>作用范围 当前行:s,全文:%s,选区在visual 选择区域后输入:后 vim 自动补全:’<,>’s 或手动指定:4,6s 或.,+2s 接下来 2 行</p></li><li><p>替换标志 </p><ul><li>i 表示大小写不敏感,I 表示敏感,等于与模式中的\c<br> :%s/foo/bar/i === :%s/foo\c/bar</li><li>g 全局替换</li><li>c 表示需要确认,每次替换系统按键确认 y 替换/n 不替换/a 替换所有/q 退出查找模式/ l 替换当前位置并退出</li></ul></li><li><p>在normal模式下按下*即可查找光标所在单词(word)</p></li><li>\c表示大小写不敏感查找,\C表示大小写敏感查找</li><li><em># 匹配当前光标所在的单词 </em>是下一个 #是上一个</li><li>光标移动可以与其他命令联动,比如 0y$ 表示从行首拷贝到最后一个字符</li><li>拷贝不一定要用y,还可以使用d(删除),v(可视化选择),gU(变大写),gu(变小写)</li></ul><h4 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h4><ul><li>i在光标前插入(insert),I 在当前行开头插入</li><li>a在光标后插入(append),A在当前行结尾进行插入,相当于执行了<code>$a</code>操作</li><li>o在当前行后插入新行, O在当前行前插入新行</li><li>ea 在当前行结尾插入(end append)</li><li>cw 当前光标到单词结尾清空并插入</li><li>在插入模式删除除了使用退格键,还可以使用 ctrl+w 删除前一个单词或使用 ctrl + u 删除至行首.</li></ul><h4 id="替换和删除"><a href="#替换和删除" class="headerlink" title="替换和删除"></a>替换和删除</h4><ul><li>r 替换一个字符 replace,R 可以替换多个字母.</li><li>cc 替换一行</li><li>cw 替换一个 word</li><li>c$ 替换到行尾</li><li>s 修改一个字符</li><li><p>S 修改一整行</p></li><li><p>x 删除当前光标所在的一个字符</p></li><li>dl 删除当前字符 === x</li><li>dw 删除到下一个单词开头</li><li>d0 删除至行首</li><li>dd 删除当前行</li><li>dj 删除上一行</li><li>dk 删除下一行</li><li>gg dG 删除全部内容</li><li>:1,10d 删除1-10行</li><li>:11,$d 删除11行及以后所有行<br>删除的内容存到剪贴板.</li></ul><h4 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h4><p>d,c,y等操作符加 一个动作命令组成一次操作.例如 daw.<br> 操作符有</p><pre><code>- c 修改 (change)- d 删除 (delete)- y 复制, (yank)- p 粘贴 (paste/put)- g~ 反转大小写- gu 转换为小写- gU 转换为大写- `>` 增加缩进- `<` 减少缩进- `=` 自动缩进</code></pre><h4 id="复制粘贴"><a href="#复制粘贴" class="headerlink" title="复制粘贴"></a>复制粘贴</h4><ul><li>yy 拷贝当前行(yank)</li><li>按v或V进入可视模式,然后上下左右选择,再按y即可复制,按d即可剪贴</li><li>p 粘贴到下一行或右侧</li><li>P 粘贴至上一行或左侧</li><li>调换文本行可以使用 ddp(先删除当前行再粘贴),同理复制当前行 yyp</li><li>交换两个词. 先用 d 删除一个词,然后 mm 标记,在用 v 选中另一个词按 p替换,然后 ~m 回到标记位置再按 p 即可完成替换.</li><li>针对字符的复制或删除操作如 x,diw等将创建面向字符的寄存器,粘贴时会放在光标后.针对行的复制删除操作如 dd,yy 或 dap 会创建针对行的寄存器,粘贴时会放在当前行的下一行.</li></ul><h4 id="撤销和重复"><a href="#撤销和重复" class="headerlink" title="撤销和重复"></a>撤销和重复</h4><ul><li>u撤销最近一次修改(undo)</li><li>.命令可以再普通模式下重复上次修改,如果想要在命令模式下重复上次命令可以用<code>@:</code></li><li>U撤销所有</li><li>ctrl + r 取消最后一次的撤销</li></ul><h4 id="文件管理"><a href="#文件管理" class="headerlink" title="文件管理"></a>文件管理</h4><ul><li>:e<path to="" file=""></path> 打开一个文件</li><li>:saveas<path to="" file=""></path> 文件另存为</li><li>:x 或 zz 或:wq 保存并推出</li><li>:bn 切换到下一个文件(n表示next,b表示切换blocked-out)</li><li>:bp 切换到上一个文件(p表示prev)</li><li>:n 切换到下一个</li></ul><h4 id="命令行模式技巧"><a href="#命令行模式技巧" class="headerlink" title="命令行模式技巧"></a>命令行模式技巧</h4><ul><li>DTc 删除光标的c之间的所有字符</li><li>Rc 将光标的字符替换为c</li><li>nDD 删除n行数据</li><li>nYY 复制n行数据</li><li>nX 删除n个字符</li></ul><h4 id="命令模式"><a href="#命令模式" class="headerlink" title="命令模式"></a>命令模式</h4><p>按:进入命令模式.</p><ul><li>:n 光标移动到多少行</li><li>:1,5w file 将第一行至第五行写入文件</li><li>:.,$w file 当前行至结束写入文件</li><li>:w 保存</li><li>:wq 保存退出</li><li>:q 退出</li><li>:q! 强制退出</li><li>:/str/ 正向搜索,光标移动到下一个结果</li><li>:?str? 逆向搜索</li><li>正则:</li><li>:/^xxx/</li><li>:/xxx$/</li><li>:s实现替换</li><li>delete 删除</li><li>yank 复制</li><li>put 粘贴</li><li>copy 拷贝</li><li>move 移动</li><li>join 连接</li><li>normal 可以执行普通模式下的命令</li><li>substitute/{pattern}/{string}/{flags} 在范围内出现{pattern}的地方替换为{string}</li><li>x,y 表示 x-y 行,<code>.</code>表示当前行,<code>%</code>表示当前文件所有行</li><li>使用<code>:t</code>和<code>:m</code>命令复制和移动行.其中<code>:t</code>是 copy 命令的简写(可以理解为copyTo),<code>:m</code>是 move 命令的简写</li></ul><h3 id="vim-实用技巧"><a href="#vim-实用技巧" class="headerlink" title="vim 实用技巧"></a>vim 实用技巧</h3><ul><li><p>分隔符文本对象.<br> vim 的文本对象由两个字符组成,第一个字符是 i/a, i 会选择分隔符内部文本,a 会选择包括分隔符在内的整个文本.可以理解为 inside/all. 第二个字符是分隔符如<code>)]}>'"</code>,t 表示标签</p><ul><li>a)/i)</li><li>a”/i”</li><li>at/it<br>文本对象前可使用 <code>d/c/y/g~/gu/gU/>/</=</code> 等操作符</li></ul></li><li><p>范围文本对象</p><ul><li>aw 当前单词及一个空格/iw 当前单词</li><li>as 当前句子及一个空格/is 当前句子</li><li>ap 当前段落及一个空行/ ip 当前段落<br>一般 d 和 a 配合, c 和 i 配合.</li></ul></li><li><p>标记位置<br> mm 命令可以标记一个位置,然后~m回到标记点 可以回到上次修改位置.<br> 比如我们想要修改一对大括号,可以先用%匹配大括号,这时 vim 会自动为发生跳转的地方设置一个标记,在我们使用 r]修改后大括号后,按 ~~ 调回到前大括号,然后 r[ 修改完成.</p></li><li><p>自定义宏</p><ul><li>创建宏 q a,操作,q 结束. 其中 a 为命名的寄存器.</li><li>查看宏 :reg a</li><li>调用宏 @a, @@可以回放上次调用的宏.</li><li>当前行多次调用宏. 3@a ,调用三次宏 a</li><li>多行调用宏, 按 v 进入可视模式,选中执行区间,<code>:normal @a</code>就可以在每一行执行宏了.</li></ul></li><li><p>用迭代求值的方式给列表编号</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">:<span class="built_in">let</span> i = 1 <span class="comment">#定义变量</span></span><br><span class="line">qa <span class="comment"># 定义宏和寄存器</span></span><br><span class="line">I ctrl+r =1 ctrl+R ) esc <span class="comment"># 对行进行操作</span></span><br><span class="line">:<span class="built_in">let</span> i += 1 <span class="comment"># 变量递增</span></span><br><span class="line">q <span class="comment"># 完成录制</span></span><br></pre></td></tr></table></figure></li><li><p>搜索引擎<br><code>\v</code>表示模式开关,可以让搜索时不用正则写过多的转义字符,比如要匹配 css 中的颜色可以使用<code>/\v #[0-9a-fA-F]{6}|[0-9a-fA-F]{3}</code><br><code>\V</code>表示原义开关,按照字符进行匹配,即便字符中有像.这样的符号也会按照原义查找.</p></li><li><p>substitude命令<br>该命令可以提供查找模式以及替换字符串,还可以指定执行的范围.具体语法<code>:s/{pattern}/{string}/{flags}</code> 在范围内出现{pattern}的地方替换为{string},如果想要全局配置要使用<code>:%s/xxx/xxx/g</code><br>flags 为标志位,其中 g 是全局范围,n 为统计个数而不执行替换,c 让我们确认每一次修改.e 可以屏蔽错误提示.i 表示大小写不敏感,I 表示大小写敏感.</p></li></ul><h4 id="分屏"><a href="#分屏" class="headerlink" title="分屏"></a>分屏</h4><ul><li>:vsp 左右分屏</li><li>:sp 上下分屏</li><li>:tabnew 新建标</li></ul><h3 id="z和fzf脚本"><a href="#z和fzf脚本" class="headerlink" title="z和fzf脚本"></a>z和fzf脚本</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">j</span></span>() {</span><br><span class="line"> <span class="keyword">if</span> [[ -z <span class="string">"$*"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">cd</span> <span class="string">"<span class="variable">$(_z -l 2>&1 | fzf +s | sed 's/^[0-9,.]* *//')</span>"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> _last_z_args=<span class="string">"<span class="variable">$@</span>"</span></span><br><span class="line"> _z <span class="string">"<span class="variable">$@</span>"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">jj</span></span>() {</span><br><span class="line"> <span class="built_in">cd</span> <span class="string">"<span class="variable">$(_z -l 2>&1 | sed 's/^[0-9,.]* *//' | fzf -q $_last_z_args)</span>"</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="vim入门指南"><a href="#vim入门指南" class="headerlink" title="vim入门指南"></a>vim入门指南</h1><h3 id="快捷操作"><a href="#快捷操作" class="headerlink" titl
</summary>
<category term="VIM" scheme="http://yoursite.com/categories/VIM/"/>
<category term="VIM" scheme="http://yoursite.com/tags/VIM/"/>
</entry>
<entry>
<title>入门shell编程</title>
<link href="http://yoursite.com/2019/01/11/%E5%85%A5%E9%97%A8shell%E7%BC%96%E7%A8%8B/"/>
<id>http://yoursite.com/2019/01/11/入门shell编程/</id>
<published>2019-01-11T11:20:45.000Z</published>
<updated>2021-08-30T11:21:42.942Z</updated>
<content type="html"><![CDATA[<h1 id="入门shell编程"><a href="#入门shell编程" class="headerlink" title="入门shell编程"></a>入门shell编程</h1><h3 id="第一个shell脚本"><a href="#第一个shell脚本" class="headerlink" title="第一个shell脚本"></a>第一个shell脚本</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line">mkdir code</span><br><span class="line"><span class="built_in">cd</span> code</span><br><span class="line"><span class="keyword">for</span> ((i=0; i<3; i++)); <span class="keyword">do</span></span><br><span class="line"> touch test_<span class="variable">${i}</span>.txt</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"shell很简单"</span> >> test_<span class="variable">${i}</span>.txt</span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>让我们看一下以上代码都做了什么:</p><ol><li>从系统path中寻找指定脚本的解释程序 </li><li>创建 名叫code文件夹 </li><li>进入创建的文件夹 </li><li>for循环3次 </li><li>创建文件 </li><li>往创建的文件中写入信息 </li><li>结束循环</li></ol><p>mkdir, touch,cd,touch,echo都是系统命令,在命令行下可以直接执行 for, do, done 是shell脚本语言 for循环的语法.</p><h3 id="编写shell"><a href="#编写shell" class="headerlink" title="编写shell"></a>编写shell</h3><p>新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php,扩展名为php,如果你用Python,扩展名为python.</p><p>第一行一般是这样:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/php</span></span><br><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"><span class="comment">#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行 /env 是系统的PATH目录中查找</span></span><br></pre></td></tr></table></figure><h4 id="运行shell脚本有两种方式"><a href="#运行shell脚本有两种方式" class="headerlink" title="运行shell脚本有两种方式"></a>运行shell脚本有两种方式</h4><ol><li><p>作为可执行程序</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">chmod +x op_base.sh //设置op_base.sh可执行权限</span><br><span class="line">./op_base.sh //执行op_base.sh文件</span><br></pre></td></tr></table></figure></li><li><p>作为参数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/bin/sh op_base.sh</span><br></pre></td></tr></table></figure></li></ol><h4 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h4><p>定义变量时,变量名前不需要加符号和Python一样,但是在PHP语言中变量需要加$.如<code>my_name="jack"</code></p><p>变量名和=之间不能有空格且变量后不能有<code>;</code>号.</p><h4 id="使用变量"><a href="#使用变量" class="headerlink" title="使用变量"></a>使用变量</h4><p>对于已经定义过的变量,使用的时候在前面添加<code>$</code>,如<code>echo $my_name</code>,或<code>echo ${my_name}</code> .花括号是可选的,但建议加上.因为花括号是为了帮助解释器识别变量的边界.</p><h4 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h4><p>单行注释使用<code>#</code>,解释器会忽略该行</p><p>sh 里没有多行注释,只能每一行加一个#号.</p><h4 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h4><p>shell 不像其他语言如 php,python 有很多数据类型,在 shell 中常用的数据类型即字符串和数字.</p><p>shell中的引号和 php 类似,可以用单引号或双引号,双引号中可以有变量,可以出现转义字符,但单引号中的变量是无效的.python 中引号没有区别,且存在三个引号.三个引号不会被转义.</p><h5 id="字符串操作"><a href="#字符串操作" class="headerlink" title="字符串操作"></a>字符串操作</h5><ul><li><p>拼接字符串</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a=<span class="string">"hello"</span></span><br><span class="line">b=<span class="string">"world"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$a</span> <span class="variable">$b</span></span><br></pre></td></tr></table></figure></li></ul><ul><li><p>获取字符串长度</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="variable">${#a}</span></span><br></pre></td></tr></table></figure></li><li><p>截取字符串</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="variable">${a:0:2}</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="shell-数组"><a href="#shell-数组" class="headerlink" title="shell 数组"></a>shell 数组</h3><h4 id="定义数组"><a href="#定义数组" class="headerlink" title="定义数组"></a>定义数组</h4><p>在 shell 中用括号表示数组,数组元素用<strong>空格</strong>分隔开.如<code>name=(name1 name2 name3)</code>还可以单独定义数组的各个分量如<code>arr[0]=name1</code></p><h4 id="读取数组"><a href="#读取数组" class="headerlink" title="读取数组"></a>读取数组</h4><p>读取数组的一般格式是<code>${数组[下标]}</code>,比如<code>echo ${name[0]}</code>,使用<code>@</code>符号可以获取数组中所有元素.例如<code>echo $name[@]</code></p><h4 id="获取数组长度"><a href="#获取数组长度" class="headerlink" title="获取数组长度"></a>获取数组长度</h4><p>获取数组长度与字符串类似,例如:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#获取数组元素个数</span></span><br><span class="line">length=<span class="variable">${#name[@]}</span></span><br><span class="line"><span class="built_in">echo</span> length</span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">length=<span class="variable">${#name[*]}</span></span><br><span class="line"><span class="built_in">echo</span> length</span><br><span class="line"><span class="comment"># 取得数组单个元素的长度</span></span><br><span class="line">length=<span class="variable">${#name[n]}</span></span><br><span class="line"><span class="built_in">echo</span> length</span><br></pre></td></tr></table></figure><h3 id="shell-传递参数"><a href="#shell-传递参数" class="headerlink" title="shell 传递参数"></a>shell 传递参数</h3><p>我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">./test.sh 1 2 3</span><br><span class="line"><span class="variable">$0</span> <span class="comment"># ./test.sh</span></span><br><span class="line"><span class="variable">$1</span> <span class="comment"># 1 ...</span></span><br></pre></td></tr></table></figure></p><h3 id="shell-运算符"><a href="#shell-运算符" class="headerlink" title="shell 运算符"></a>shell 运算符</h3><p>原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 算术运算符</span></span><br><span class="line"><span class="comment"># + - \* / % = == !=</span></span><br><span class="line">val=`expr 2 + 2`</span><br><span class="line"><span class="built_in">echo</span> <span class="string">"两数之和为 : <span class="variable">$val</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#关系运算符</span></span><br><span class="line"><span class="comment"># -eq 相等 , -ne 不相等, -gt 大于, -lt 小于, -ge 大于等于, -le 小于等于</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$a</span> -eq <span class="variable">$b</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">$a</span> -eq <span class="variable">$b</span> : a 等于 b"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 布尔运算</span></span><br><span class="line"><span class="comment"># ! 非. -o 或, -a与</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$a</span> -lt 100 -a <span class="variable">$b</span> -gt 15 ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">$a</span> 小于 100 且 <span class="variable">$b</span> 大于 15 : 返回 true"</span></span><br><span class="line"><span class="comment"># 逻辑运算</span></span><br><span class="line"><span class="comment"># && ||</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 字符串运算</span></span><br><span class="line"><span class="comment"># =, !=, -z 长度是否为 0, -n 长度是否不为 0, $ 是否为空</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件测试运算符</span></span><br><span class="line"><span class="comment"># -e 是否存在</span></span><br><span class="line"><span class="comment"># -d 是否存在且为目录</span></span><br><span class="line"><span class="comment"># -f 是否存在且为常规文件</span></span><br><span class="line"><span class="comment"># -r 是否存在且可读</span></span><br><span class="line"><span class="comment"># -w 是否存在且可写</span></span><br><span class="line"><span class="comment"># -x 是否存在且可执行</span></span><br><span class="line"><span class="comment"># -s 文件大小是否大于 0</span></span><br></pre></td></tr></table></figure><h3 id="test-命令-和"><a href="#test-命令-和" class="headerlink" title="test 命令 和 [[]]"></a>test 命令 和 [[]]</h3><p>test命令用来测试某条件是否成立,可以用[]代替, 但[]内部不能使用||和&&以及!,[[]]支持这种写法,<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">read</span> tel</span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$tel</span> =~ ^1[0-9]{10}$ ]]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"你输入的是手机号码"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"你输入的不是手机号码"</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure></p><h3 id="echo-和-printf"><a href="#echo-和-printf" class="headerlink" title="echo 和 printf"></a>echo 和 printf</h3><p>echo 和 printf是用于字符串的输出.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="variable">$1</span>"</span> > xx.txt</span><br><span class="line"><span class="built_in">printf</span> <span class="string">"%-10s %-8s %-4s\n"</span> 姓名 性别 体重kg</span><br></pre></td></tr></table></figure></p><h3 id="shell-流程控制"><a href="#shell-流程控制" class="headerlink" title="shell 流程控制"></a>shell 流程控制</h3><p><strong>和其他编程语言流程控制不同,sh的流程控制不能为空</strong> </p><p>如 js 流程控制</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="literal">true</span></span><br><span class="line"><span class="keyword">if</span> a{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"a 为 true"</span>)</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"> <span class="comment">//什么也不做</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 在 sh/bash 里不能这么写,如果 else 分支没有执行语句就不要写 else. 如:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env sh</span></span><br><span class="line">a=1</span><br><span class="line">b=2</span><br><span class="line"><span class="keyword">if</span> [<span class="variable">$a</span> == <span class="variable">$b</span>]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">'a 等于 b'</span></span><br><span class="line"><span class="keyword">elif</span> [<span class="variable">$a</span> -gt <span class="variable">$b</span>]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">'a 大于 b'</span></span><br><span class="line"><span class="keyword">elif</span> [<span class="variable">$a</span> -lt <span class="variable">$b</span>]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">'a 小于 b'</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"没有符合的条件"</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><h4 id="for-循环"><a href="#for-循环" class="headerlink" title="for 循环"></a>for 循环</h4><p>shell 的 for 循环和 python 类似.</p><p>python 的 for 循环:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>:</span><br><span class="line"> print(item)</span><br></pre></td></tr></table></figure><p>shell 的 for 循环写法一:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">for item in 1 2 3 4 5;</span><br><span class="line">do</span><br><span class="line">echo 'i='$i</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p>shell 的 for 循环写法二:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">for ((i=0;i<5;i++)); </span><br><span class="line">do</span><br><span class="line">echo "i="$i</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h4 id="while语句"><a href="#while语句" class="headerlink" title="while语句"></a>while语句</h4><p>while 循环用于不断执行一系列命令,也用于从输入文件中读取数据,命令通常为测试条件.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">int=1</span><br><span class="line"><span class="keyword">while</span> ((<span class="variable">$int</span><=5))</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$int</span></span><br><span class="line"><span class="built_in">let</span> <span class="string">"int++"</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数</p><h3 id="shell-函数"><a href="#shell-函数" class="headerlink" title="shell 函数"></a>shell 函数</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">funWithReturn(){</span><br><span class="line"> echo "这个函数会对输入的两个数字进行相加运算..."</span><br><span class="line"> echo "输入第一个数字: "</span><br><span class="line"> read aNum</span><br><span class="line"> echo "输入第二个数字: "</span><br><span class="line"> read anotherNum</span><br><span class="line"> echo "两个数字分别为 $aNum 和 $anotherNum !"</span><br><span class="line"> return $(($aNum+$anotherNum))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="输出-输入重定向"><a href="#输出-输入重定向" class="headerlink" title="输出/输入重定向"></a>输出/输入重定向</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># > 输出重定向到 file</span></span><br><span class="line"><span class="comment"># < 输人重定向到 file</span></span><br><span class="line"><span class="comment"># >> 追加</span></span><br><span class="line"><span class="comment"># n>&m n 和m 合并到 m</span></span><br></pre></td></tr></table></figure><h3 id="shell-结合系统命令"><a href="#shell-结合系统命令" class="headerlink" title="shell 结合系统命令"></a>shell 结合系统命令</h3><p>shell脚本结合系统命令会更加强大,在字符串处理领域,有 grep,awk,sed 三剑客, grep 负责找出特定的行, awk 能将行拆分成多个字段, sed 则可以实现更新插入删除等写操作.例如定时检测 nginx,mysql 是否被关闭.</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">path=/var/<span class="built_in">log</span></span><br><span class="line"><span class="built_in">log</span>=<span class="variable">${path}</span>/httpd-mysql.log</span><br><span class="line"></span><br><span class="line">name=(apache mysql)</span><br><span class="line"></span><br><span class="line">exs_init[0]=<span class="string">"service httpd start"</span></span><br><span class="line">exs_init[1]=<span class="string">"/etc/init.d/mysqld restart"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> ((i=0; i<2; i++)); <span class="keyword">do</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"检查<span class="variable">${name[i]}</span>进程是否存在"</span></span><br><span class="line"> ps -ef|grep <span class="variable">${name[i]}</span> |grep -v grep</span><br><span class="line"> <span class="keyword">if</span> [ $? -eq 0 ]; <span class="keyword">then</span></span><br><span class="line"> pid=$(pgrep -f <span class="variable">${name[i]}</span>)</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"`date +"</span>%Y-%m-%d %H:%M:%S<span class="string">"` <span class="variable">${name[$i]}</span> is running with pid <span class="variable">$pid</span>"</span> >> <span class="variable">${log}</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> $(<span class="variable">${exs_init[i]}</span>)</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"`date +"</span>%Y-%m-%d %H:%M:%S<span class="string">"` <span class="variable">${name[$i]}</span> start success"</span> >> <span class="variable">${log}</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="comment">#检测 nginx、mysql进程是否存在,如果不存在了会自动重新启动。 脚本每次运行会写日志的,没事可以去看看该日志文件,如果进程是不是真的经常性不存在,恐怕就要排查一下深层原因了。</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="入门shell编程"><a href="#入门shell编程" class="headerlink" title="入门shell编程"></a>入门shell编程</h1><h3 id="第一个shell脚本"><a href="#第一个shell脚本" cla
</summary>
<category term="Shell" scheme="http://yoursite.com/categories/Shell/"/>
<category term="Shell" scheme="http://yoursite.com/tags/Shell/"/>
</entry>
</feed>