-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
753 lines (753 loc) · 286 KB
/
search.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
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[读书:你不知道的JS]]></title>
<url>%2F2019%2F08%2F31%2F%E8%AF%BB%E4%B9%A6%EF%BC%9A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JS%2F</url>
<content type="text"><![CDATA[【你不知道的JS】一共上中下三册,包含内容有:作用域和闭包、this和对象原型、类型和语法、异步和性能、ES6及更新版本等内容,整理本篇博客目的主要是整合所学基础知识 作用域和闭包引擎、编译器、作用域代码执行前的编译会经历的三个步骤: 词法分析:将字符组成的字符串分解成有意义的代码块(词法单元),会有特定步骤进行性能优化 语法分析:将词法单元转化成一个抽象语法树(AST),会有特定步骤进行性能优化 生成代码:将AST转化为可执行代码 引擎、编译器、作用域的任务: 引擎:负责JS的编译、执行过程 编译器:负责语法分析、代码生成 作用域:收集维护变量、有一套严格的规则(如:词法作用域) 当你给一个变量(函数)赋值时: 声明:编译器询问作用域是否存在该变量,如没有,则进行声明 查找:运行时引擎在作用域查找该变量,找到就给它赋值 引擎查找类型分为 LHS 和 RHS: LHS:找到变量容器并进行赋值(非严格模式下找不到变量时会自动声明,否则抛出ReferenceError) RHS:找到变量的源值(找不到时会抛出ReferenceError、查找到变量但是进行不合理的操作时会抛出TypeError)1234function foo(a) { console.log(a) // RHS}foo(2) // foo 使用了 RHS,2 是对变量 a 进行赋值所以使用的是 LHS 作用域嵌套: 引擎无法在当前作用域查找到变量时,会向上一级继续查找,直到最外层的全局作用域 作用域规则–词法作用域作用域的两种工作模型: 动态作用域:运行时确定,关注在何处调用(和JS中的this很像) 词法作用域:定义时确定,关注在何处声明,只会查找一级标识符,如:a.b.c,只会查找a(JS所使用的) 欺骗词法作用域的方法: eval(...):非严格模式下:eval 所执行的代码如果包含变量(函数)声明,在运行时能对词法作用域进行修改,严格模式下 eval 有自己的作用域 with(...):非严格模式下:通常被当作重复引用同一个对象中多个属性的快捷方式,实际是根据传递的对象创建新的词法作用域,严格模式下会被禁止12345function foo(str, a) { eval(str) // 在执行时声明了 b console.log(a, b) // 2 3}foo("var b = 3", 2) 欺骗词法作用域导致的性能问题: 问题:引擎无法在编译时对作用域查找进行优化,导致代码运行变慢 原因:有些依赖于对词法进行静态分析,预先确定变量函数定义的位置,以便快速找到标识符,但是如果发现eval和with,引擎只能假设标识符的位置判断都是无效的 函数作用域和块作用域什么是函数作用域和块作用域: 函数作用域:有作用域气泡,这个函数的全部变量都可以在函数范围内使用及复用 块作用域:ES6 中的const和let可声明块级作用域,在块{}范围内使用和复用,除此之外还有try/catch,with 函数作用域可以用来做什么? 隐藏代码:把变量和函数包裹在一个函数作用域中,用这个作用域隐藏它们 隐藏代码的好处: 规避冲突:避免同名标识符之间的冲突(规避冲突的两种解决方式:全局命名空间、模块管理工具) 函数名本身会污染所在全局作用域,怎么解决? 使用立即执行函数表达式(function foo() {..})(),将变量名隐藏在自身中(此时foo只能在...处被访问),也称IIFE(也可以是匿名) 区分函数声明和函数表达式: 函数声明:function是声明的第一个词,名称标识符绑定在所在作用域 函数表达式:function不是声明的第一个词,名称标识符绑定在函数自身 变量提升 变量提升:即所有声明(变量和函数)被移动到各自作用域的最顶端(const和let不存在变量提升) 提升顺序:函数会先提升,然后是变量 闭包 什么是闭包:当一个函数记住并访问所在词法作用域时就产生了闭包,即使函数是在当前词法作用域之外执行 闭包的作用:保持对一个作用域的引用,使作用域一直存活 回调函数与闭包:定时器、事件监听器、Ajax请求、跨窗口通信、web workers或其他异步(或同步任务)中,只要使用了回调函数,实际上就是在使用闭包 模块与闭包:调用模块中的方法时,实际就是在使用闭包,没有闭包的模块不是真正的模块 循环与闭包:循环过程中每个迭代都需要一个闭包作用域1234567891011121314151617181920212223242526272829303132333435363738394041424344for(var i = 1; i < 4; i++) { setTimeout(() => { console.log(i) // 4 4 4 }, i*1000)}// 利用 IIFE 创建一个作用域,并创建一个变量用来保存每个迭代中的 ifor(var i = 1; i < 4; i++) { (function() { var j = i setTimeout(() => { console.log(j) // 1 2 3 }, j*1000) })()}for(var i = 1; i < 4; i++) { (function(j) { setTimeout(() => { console.log(j) // 1 2 3 }, j*1000) })(i)}// 给 setTimeout 传递第三个参数,定时器到期就传递给定时器中的函数for(var i = 1; i < 4; i++) { setTimeout((j) => { console.log(j) // 1 2 3 }, i*1000, i)}// 使用 let 劫持作用域for(var i = 1; i < 4; i++) { let j = i setTimeout(() => { console.log(j) // 1 2 3 }, j*1000)}for(let i = 1; i < 4; i++) { setTimeout(() => { console.log(i) // 1 2 3 }, i*1000)} this和对象原型关于this this是在运行中被调用时绑定的,它不指向函数本身,也不指向函数的词法作用域 函数如何引用自身?第一种:具名函数可在函数内部通过函数名引用自身,第二种:匿名函数可通过arguments.callee引用自身(被弃用了),第三种:可通过call将this绑定在自身上 匿名函数的缺点?第一:调试栈更难追踪,第二:自我引用(递归、事件绑定解除等)更难,第三:代码(稍微)更难理解 this的绑定规则 默认绑定:非严格模式下绑定到全局对象,严格模式下绑定大到undefined(函数体是否严格,而非调用位置) 隐式绑定:如果调用位置有上下文对象,会绑定到这个上下文对象上。注意⚠️:隐式绑定后不能再赋值给另一个变量进行调用,否则会造成隐式丢失,如:a.b中b的this指向a,c = a.b中b的this就会指向全局而不是a,解决办法:使用硬绑定c = b.bind(a)可使b中的this指向a 显式绑定:使用bind、call、apply硬绑定指定this,但是如果传入null`undefined会被忽略,会进行默认绑定,所以如果显式绑定一个空对象,可以使用Object.create(null)`创建一个没有原型的空对象传进去 new绑定:构造函数(使用new时被调用的函数)中的this指向它的实例对象 如何确定this应用于哪条绑定规则?先通过调用位置判断,如果一个位置可以应用多条规则,则通过优先级确定 优先级:默认绑定 < 隐式绑定 < 显式绑定 < new绑定 注意⚠️:调用间接引用的函数会应用默认绑定的规则 固定this:回调函数造成的this丢失可以通过固定this来解决,第一种:词法作用域风格–箭头函数,第二种:词法作用域风格–将this赋值给一个变量,第三种:this风格的绑定bind(this),存在词法作用域风格的代码和this风格,在代码最好只保持一种风格 使用new时会发生什么? 创建一个新对象(空对象) 新对象的原型指向构造函数的原型 新对象绑定到构造函数中的this 返回新对象 关于对象 定义方式:声明形式、构造形式 属性名:永远是字符串,且可通过[变量 + 变量]形式计算出来 复制:浅拷贝(Object.assign({}, obj)) + 深拷贝 对象属性: 属性描述符:设置属性的特性,writable: false使属性不可修改,configurable: false使属性不可重定义、不可删除,enumerable: false使属性不可枚举。相关博客:Object.defineProperty和Proxy 查看属性描述符:Object.getOwnPropertyDescriptor(obj, 'key') 禁止对象扩展:Object.preventExtensions(obj)使对象不可添加新属性,但是现有属性可修改、可删除。判断是否可扩展:Object.isExtensible(obj) 密封对象:Object.seal(obj)使对象不可添加新属性,现有属性不可重定义、不可删除,但是可修改(相当于Object.preventExtensions(obj) + configurable: false)。判断是否密封:Object.isSealed(obj) 冻结对象:Object.freeze(obj)使对象不可添加新属性,现有属性不可重定义、不可删除、不可修改(相当于Object.seal(obj) + writable: false)。判断是否冻结:Object.isFrozen(obj) 获取对象的属性:Object.keys()返回对象自身的可枚举的属性 遍历对象: for in:遍历的key,检查自身 + 原型链中的属性,遍历可枚举的属性 for of:遍历的是value,向对象请求一个迭代器,使用迭代器的next()方法进行遍历,对象没有内置迭代器,所以需要自定义对象迭代器@@iterater(原型中存在Symbol.iterator属性),相关博客:JS使用小技巧整理–创建一个可迭代对象 对象存取值: 取值:触发[Get]。查找属性步骤:存在存取操作符getter?获取getter的返回值 => 在对象中找 => 在原型中找 => 返回undefined 赋值:触发[Put]。检查操作属性:存在存取操作符setter?调用setter进行赋值 => writable为false?赋值无效 => 都不是?赋值成功 检查属性是否存在: hasOwnProperty():只会检查自身,不会检查原型,所以不能判断没有原型的对象如Object.create(null)用法:Object.prototype.hasOwnProperty.call(obj,'属性名') 示例 使用in操作符:检查自身 + 原型链中的属性,包括不可枚举的(如果是数组:只会检查下标) 关于类 类是一种设计模式,使用new可以将类实例化 面向类的设计模式:实例化、继承、多态 混入模式(mixin):可以用来模拟类的复制行为,但是有很多隐患 原型[[prototype]] 对象之间通过内部的[[prototype]]关联 所有[[prototype]]的终点都是Object.prototype 修改prototype后,新prototype的constructor属性(不可枚举、可修改)不会自动获得,需要手动赋值 Object.create() 创建一个对象,并把这个对象的[[prototype]]关联到指定的对象 Object.create(null)会创建一个没有原型链的对象,不会受原型链干扰,适合存储数据 Object.create()的polyfill12345Object.create = function(obj) { function F() {} F.protoptype = obj return new F()} Object.getPrototypeOf()、Object.setPrototypeOf()、isPrototypeOf()123456789101112// Object.getPrototypeOf() 查看某个对象的原型function A() {}let a = new A()Object.getPrototypeOf(a) === A.prototype a.__proto__ === A.prototype // __proto__ 非标准// isPrototypeOf() 测试一个对象是否存在于另一个对象的原型链上A.prototype.isPrototypeOf(a)// Object.setPrototypeOf() 关联两个对象的原型a.prototype = Object.create(b.prototype) // ES5 抛弃默认的a.prototype(需要进行垃圾回收)Object.setPrototypeOf(a.prototype, b.prototype) // ES6 直接修改现有的a.prototype 行为委托 内部委托比起直接委托可以让API接口设计更加清晰 行为委托认为对象之间的兄弟关系,相互委托,而非父子关系,[[prototype]]机制本质上就是行为委托 两种设计模式:面向对象风格(类风格,构造函数、原型、new)、对象关联风格(委托风格,直接穿件和关联对象,使用基于[[prototype]]的行为委托,干净简洁) ES6的Class是类风格的语法糖 类型和语法我的另外几篇关于数据类型的博客:数据类型介绍 数据类型判断 数据类型转换 异步和性能ES6及更新版本等内容具体可参考阮一峰大大的 ECMAScript入门]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HTTP相关知识整理]]></title>
<url>%2F2019%2F07%2F25%2FHTTP%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[HTTP相关知识整理 TCP/IP 协议(应用层、传输层、网络层、链路层) 应用层:规定了向用户提供应用服务时通信的协议,如DNS域名系统、FTP文件传输协议、HTTP协议、HTTPS协议 传输层:对接上层应用层,提供处于网络连接中两台计算机之间的数据传输,如TCP传输控制协议、UDP用户数据报协议、QUIC协议 网络层:规定了数据通过怎样的传输路线到达对方计算机传送给对方,如IP协议 链路层:处理连接网络的硬件部分,包括控制操作系统、硬件的设备驱动、NIC,及光纤等物理可见部分 请求的方法注意点:浏览器发送 CORS(跨域) 请求时, 分为简单请求与复杂请求,如果是复杂请求,浏览器会预先发送一个 option 请求 GET:一般用于获取服务器资源,能缓存、参数保存在浏览器历史中、因为是明文传输且参数被保存所以比POST安全性差一点 POST:一般用于传输实体主体,后退按钮/刷新数据会被重新提交、不能缓存、参数不会保存在浏览器历史中 PUT:一般用于传输文件 DELETE:用于删除文件 HEAD:用于获取报文首部,不返回报文主体 OPTIONS:用于询问请求URI资源支持的方法 HTTPS 握手过程 客户端使用 https 的 url 访问服务器,要求与服务器建立 ssl 连接 服务器收到客户端请求后, 将网站的证书(包含公钥)传送一份给客户端,服务器自己保留私钥 客户端收到网站证书后检查证书的颁发机构是否受客户端信任、校验证书是否被吊销(通过 CRL 或 OCSP 的方式)、校验证书是否在有效期内(对比系统时间)、证书的网站域名是否与证书颁发的域名一致(通过校验对方是否存在证书的私钥), 如果没有问题就随机产生一个秘钥 客户端利用公钥将会话秘钥加密, 并传送给服务器, 服务器利用自己的私钥解密出会话秘钥 后续服务器与客户端使用秘钥进行加密传输 HTTPS 中间人攻击及防御攻击过程如下: 服务器向客户端发送公钥 攻击者截获公钥,保留在自己手上 然后攻击者自己生成一个【伪造的】公钥,发给客户端 客户端收到【伪造的】公钥后,生成加密的秘钥发给服务器 攻击者获得加密的秘钥,用自己的私钥解密获得真秘钥 同时生成【假】的加密的秘钥,发给服务器 服务器用私钥解密获得【假】秘钥 服务器用【假】秘钥加密传输信息 防范方法: 通过权威的证书颁发机构 CA :服务器在发送浏览器的公钥中加入 CA 证书,浏览器可以验证 CA 证书的有效性 问题:中间人也可申请 CA 认证 HTTPS 和 HTTP 的区别 费用:HTTPS 协议需要到 CA 申请证书,免费证书很少,一般都需要一定的费用 安全:HTTP 是超文本传输协议,信息是明文传输;HTTPS 通过 SSL 加密传输协议、身份认证的网络协议,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。简单说;HTTPS = HTTP + TLS/SSL(安全传输层) 端口:HTTP 使用 80 端口; HTTPS 使用 443 端口 可配合以下博客一同食用:QUIC协议-和-TCP-UDP-协议HTTP状态码及其含义HTTP的请求及响应从点击url到浏览器显示页面,这个过程中发生了什么?]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTTP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS使用小技巧整理]]></title>
<url>%2F2019%2F07%2F23%2FCSS%E4%BD%BF%E7%94%A8%E5%B0%8F%E6%8A%80%E5%B7%A7%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[一些CSS使用的小技巧整理,如实现文字的两端对齐等 改变光标的颜色:caret-color 对背景进行文本裁剪:background-clip MDN 改变选择的文本颜色:::selection 文字倒序:letter-spacing(值为负数且为字号的两倍) 校验表单输入的内容::valid、:invalid配合pattern效果 使图片自适应:object-fit(类似小程序的mode)MDN 调整文本排版方向:writing-mode 修改滚动条样式:scrollbarMDN 利用伪元素绘制各种图形:网站地址 CSS 实现文字的两端对齐效果1234567891011.test { width: 100px; text-align:justify;}.test:after{ content:""; display: inline-block; width:100%; overflow:hidden; height:0;} PS:text-align-last: justify;也可实现同样的功能 效果) 实现一个等宽高的正方形效果123456789div { width: 100px; background: red; display: flex;}div::after { content: ''; padding-top: 100%;} 快速实现垂直居中效果123456789body { min-height: 100vh; display: flex;}div { height: 100px; width: 100px; margin: auto;} 渐变实现进度条效果12345678910/* 自定义变量:var(--name) 变量名:需 -- 开头*/div { --c: red; --l: 10%; height: 10px; border: 1px solid; border-radius: 5px; background: linear-gradient(var(--c), var(--c)) no-repeat; background-size: calc(var(--l));} 鼠标放元素上时元素自动转圈圈效果123456789div { height: 100px; width: 100px; background: red;}div:hover { transform: rotate(10turn); transition: all 10s;} 使用vw定制rem自适应布局123html { font-size: calc(100vw / 7.5);} 文本溢出省略12345678910111213141516// 多行.test { overflow: hidden; text-overflow: ellipsis; word-break: break-all; display: -webkit-box; -webkit-line-clamp: @n; // 值可根据行数修改 -webkit-box-orient: vertical;}// 一行.test { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; word-break: break-all;} 利用linear-gradient使背景颜色动态渐变效果1234567891011121314.test { background: linear-gradient(135deg, #FF4040, #FFA500, #ADFF2F, #40E0D0, #BF3EFF) left center/400% 400%; animation: move 10s infinite;}@keyframes move { 0%, 100% { background-position-x: left; } 50% { background-position-x: right; }} attr()获取DOM元素上的属性值效果123div::after { content: attr(test);} 禁用鼠标事件12345.disabled { pointer-events: none; cursor: default; opacity: 0.6;}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[浏览器相关知识整理]]></title>
<url>%2F2019%2F07%2F21%2F%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[整理了浏览器相关的一些知识,包括浏览器的组成、渲染引擎渲染机制等 浏览器结构组成 用户界面(除了页面显示窗口之外的其他部分) 浏览器引擎(负责通信,用户界面、渲染引擎之间传送指令,缓存中读写数据) 渲染引擎(解析 HTML 和 CSS 并渲染) 网络(网络调用或资源下载) UI 后端(绘制基本的浏览器窗口内控件) JS解释器(解释、编译、执行JS脚本) 数据存储(保存 cookie、localStorage 等各种数据) 渲染引擎的工作流程 构建 DOM 树 解析 CSS 文件,生成一个具有样式规则描述的 DOM 渲染树 将渲染树进行布局、绘制 如渲染完首屏后,对 DOM 进行操作会引起浏览器引擎对 DOM 渲染树的重新布局和重新绘制PS:CSS 规则是按照「从右向左」的方式在 DOM 树上进行逆向匹配,是为了节省效率,但是也要尽量避免在选择器末尾添加通配符。 渲染引擎相关的性能优化 减少 JS 加载对 Dom 渲染的影响 避免重排,减少重绘(减少使用 width、 margin、 padding 等影响 CSS 布局对规则,可以使用 CSS3 的 transform 代替) 减少使用关系型样式表的写法(使用唯一的类名、避免在选择器末尾添加通配符) 减少 DOM 的层级 浏览器的渲染进程 GUI渲染线程(渲染浏览器界面,与JS引擎线程是互斥) JS引擎线程(处理Javascript脚本程序,与GUI引擎线程是互斥) 事件触发线程(控制事件循环,待处理队列中的事件都得排队等待JS引擎处理) 定时触发器线程(setInterval与setTimeout所在线程,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行) 异步http请求线程(如有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由JS引擎执行) 浏览器重绘和重排 几种主流内核 Trident(1997,IE,浏览器被弃用) Gecko(2000,FireFox)、 Webkit(2001,Safari) Presto(2003,opera前内核、已废弃) Chromium(2008,Chrome前内核,基于webkit) blink(2013,Chrome,Google 和 Opera Software 共同研发) EdgeHTML(2015,Edge已重选Chromium作为内核) 浏览器重绘和重排文档初次加载时:浏览器引擎将HTML文档解析成对应的DOM树根据DOM元素的几何属性来构建一个用于渲染的渲染树,渲染树的每个节点都包含其大小和内外边距等属性(对于隐藏的不需要显示的元素,不会构建到渲染树当中)。渲染树构建完成后,浏览器就可以将元素放置到正确位置了,再根据渲染树节点的样式属性绘制到页面中。 重排(reflow) - 节点尺寸位置发生改变,对性能影响更大,会影响到其父元素、子元素和兄弟元素的重排 重绘(repaint) - 背景颜色图片、外观属性等发生改变 哪些改变能引起重排: 重新调整浏览器窗口大小 添加、删除可见的DOM元素 添加、删除样式表 激活CSS伪类,如a:hover 修改字体 修改class的属性 设置style的属性 页面渲染器初始化 计算offsetWidth和offsetHeight display: none 隐藏一个 DOM 节点-触发重排和重绘 visibility: hidden 隐藏一个 DOM 节点-只触发重绘,因为没有几何变化 如何避免重排或者减少重排带来的性能问题: 减少使用 width、 margin、 padding 等影响 CSS 布局对规则,可以使用 CSS3 的 transform 代替 避免在内联样式中设置多重属性 将动画应用在 absolute 定位或者 fixed 的元素上 减少 table 布局 批量修改 DOM DOM离线化,即给元素设置display:none后进行操作不会频繁触发重排和重绘,只会在元素添加到元素中的时候触发一次 缓存布局信息(将某个值缓存下来,避免重复读取) 浏览器的内存分配和垃圾回收HTTP 头信息控制缓存(强缓存、协商缓存)客户端请求资源先检查是否命中强缓存 强缓存通过设置两种 HTTP Header 实现: Expires 和 Cache-Control(服务器返回的) Expires(HTTP 1.0) 设置的是具体的过期日期 告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求 弊端:受限于本地时间,如果修改了本地时间,可能会造成缓存失效 Cache-Control:max-age=xxx(HTTP 1.1) 使用相对时间,指定缓存有效时长秒数,在max-age这段时间里浏览器就不会再向服务器发送请求了 Cache-Control的其他参数:no-store禁止缓存任何资源,每次都需要向服务器请求完整资源;no-cache客户端缓存内容,但每次使用都需要和服务器协商;private只有客户端可以缓存;public客户端和代理服务器都可缓存 优先级上:两者同时使用时 Cache-Control会覆盖 Expires 命中强缓存时返回 200 from cache,直接从本地获取缓存资源,不会发请求到服务器 强缓存没有命中时,客户端携带缓存标识发请求到服务器验证是否命中协商缓存 协商缓存通过设置两种 HTTP Header 实现: Last-Modified 和 ETag(服务器返回的) Last-Modified(HTTP 1.0) 值是资源在服务器上的最后修改时间 请求资源时,浏览器将上一次返回Last-Modified的值复制一份放到请求头中的If-Modified-Since中,服务器收到后,把这个值和最后修改时间做对比,如没有变化则命中缓存返回 304 弊端:本地打开缓存文件也会导致Last-Modified被修改;Last-Modified以秒计时,如果在不可感知的时间内修改文件,服务器会认为缓存时命中的 ETag(HTTP 1.1) 服务器生成的资源文件的一个唯一标识,只要资源有变化,Etag 就会重新生成 请求资源时,浏览器将上一次返回ETag的值复制一份放到请求头中的If-None-Match中,服务器收到后,把这个值和最后修改时间做对比,如没有变化则命中缓存返回 304 优先级上:两者同时使用时服务器会优先考虑 ETag;性能上,Etag要逊于Last-Modified;精确度上,Etag要优于Last-Modifie 命中协商缓存时服务器返回 304 not modified,但不返回资源,告诉客户端直接从缓存中获取;协商缓存失效服务器会返回 200 和请求结果;如果资源被删除,服务器返回 404 PS:当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;但是当f5刷新网页时,会跳过强缓存,但是会检查协商缓存 谷歌浏览器跨域浏览器图标右键–属性–在目标后面加上--disable-web-security --user-data-dir=C:\MyChromeDevUserData注意:最开始有一个空格 谷歌浏览器中快速截图 打开开发者工具 ctrl + shift + p 输入full 获取性能数据参考文章:初探 performance – 监控网页与程序性能浅谈小程序运行机制1234567891011121314151617181920212223// 计算加载时间 1000毫秒 == 1秒function getPerformanceTiming() { var performance = window.performance if (!performance) { console.log('你的浏览器不支持 performance 接口') return } var t = performance.timing var times = {} times.loadPage = t.loadEventEnd - t.navigationStart // 页面加载完成的时间 times.domReady = t.domComplete - t.responseEnd // 解析 DOM 树结构的时间 times.redirect = t.redirectEnd - t.redirectStart // 重定向的时间 times.lookupDomain = t.domainLookupEnd - t.domainLookupStart // DNS 查询时间 times.ttfb = t.responseStart - t.navigationStart // 读取页面第一个字节的时间 times.request = t.responseEnd - t.requestStart // 内容加载完成的时间 times.loadEvent = t.loadEventEnd - t.loadEventStart // 执行 onload 回调函数的时间 times.appcache = t.domainLookupStart - t.fetchStart // DNS 缓存时间 times.unloadEvent = t.unloadEventEnd - t.unloadEventStart // 卸载页面的时间 times.connect = t.connectEnd - t.connectStart // TCP 建立连接完成握手的时间 return times}getPerformanceTiming()]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Browser</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JS使用小技巧整理]]></title>
<url>%2F2019%2F07%2F15%2FJS%E4%BD%BF%E7%94%A8%E5%B0%8F%E6%8A%80%E5%B7%A7%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[一些JS使用的小技巧整理,如快速生成随机数,使对象可迭代、将多维数组展平等 将多维数组展平第一种:不指定展平级别,只展平一级1234567var arr = ['a', 'b', ['c', ['d']]]var flat1_1 = [].concat.apply([], arr)var flat1_2 = [].concat(...arr)var flat1_3 = arr.flat()console.log(flat1_1,flat1_2,flat1_3) // ["a", "b", "c", ["d"]] 第二种:不指定展平级别,能把数组完全展开12345678910// 把数组转化成字符串再转化成数组(不能指定递归深度)var flat2_1 = arr => arr.toString().split(',') // toString() 也可使用join(',')替代// 递归➕map(不能指定递归深度)var flat2_2 = arr => [].concat(...arr.map(i=>Array.isArray(i)?flat2_2(i):i))// 利用 flat() 函数var flat2_3 = arr.flat(Infinity)console.log(flat2_1(arr), flat2_2(arr), flat2_3) // ["a", "b", "c", "d"] 第三种:能指定展平级别1234567891011121314151617181920212223242526// 利用 flat() 函数var flat3_1 = arr.flat(2) // 展平两级// 普通递归(能指定递归深度)var flat3_2 = (arr, num) => { let result = [] if(!num) num = 1 function temp(arr) { arr.forEach((i,index) => { if(Array.isArray(i)) { if(num != 1) { num-- temp(i) } else { result.push(...i) } } else { result.push(i) } }) } temp(arr) return result}console.log(flat3_1,flat3_2(arr, 5)) // ["a", "b", "c", "d"] 字符串大小写取反12345678910111213var result = str => { let newStr = '' for(let i of str) { if(/[a-z]/.test(i)) { i = i.toUpperCase() } else if(/[A-Z]/.test(i)) { i = i.toLowerCase() } newStr += i } return newStr}console.log(result('aBc')) // "AbC" 快速过滤数组false值12let arr = [0,1,2,3,4,true,false,undefined,null,'']arr.filter(Boolean) 生成随机数123456789101112131415161718// 可设置随机数长度let randomId = len => Math.random().toString(36).substr(3, len)console.log(randomId(10)) // sj2ymh25xj// 生成随机颜色let randomColor = () => "#" + Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")console.log(randomColor()) // #8980eb// 生成范围随机数let randomNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + minconsole.log(randomNum(1, 10))// 数组值随机排列let arr = [0, 1, 2, 3, 4].slice().sort(() => Math.random() - .5) console.log(arr) // [2, 3, 0, 1, 4]// 获取随机数组成员let randomItem = arr[Math.floor(Math.random() * arr.length)] 生成星级评分12let startScore = rate => "★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate)console.log(startScore(5)) // "★★★★★" 获取URL查询参数1234// location.search = "?name=Nola&age=24"let params = new URLSearchParams(location.search.replace(/\?/ig, "")) params.has("Nola") // trueparams.get("age") // "24" 快速生成数组12345// 生成连续数组[...Array(5).keys()] // [0, 1, 2, 3, 4]// 生成值一样的数组Array(5).fill(1) // [1, 1, 1, 1, 1] 一个数字变成千分位1let numFormat = num => (num.toString().indexOf ('.') !== -1) ? num.toLocaleString() : num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') 用代码画一个键盘12(_=>[..."`1234567890-=~~QWERTYUIOP[]\\~ASDFGHJKL;'~~ZXCVBNM,./~"].map(x=>(o+=`/${b='_'.repeat(w=x<y?2:' 667699'[x=["BS","TAB","CAPS","ENTER"][p++]||'SHIFT',p])}\\|`,m+=y+(x+' ').slice(0,w)+y+y,n+=y+b+y+y,l+=' __'+b)[73]&&(k.push(l,m,n,o),l='',m=n=o=y),m=n=o=y='|',p=l=k=[])&&k.join``)() 取整 ~~ 和 | 0 和 >> 0对正数来说 ~~ + | 0 >> 0 运算结果与 Math.floor( ) 运算结果相同,而对于负数来说与Math.ceil( )的运算结果相同12345678~~2.5 === Math.floor(2.5) // 2~~-2.5 === Math.ceil(-2.5) // -22.5 | 0 // 2-2.5 | 0 // -22.5 >> 0 // 2-2.5 >> 0 // -2 判断是否是奇数 & 1 和 % 21234567// 判断是否是奇数var num = 4!!(num & 1) // false!!(num % 2) // falselet oddEven = num => !!(num & 1) ? "odd" : "even"console.log(oddEven(2)) 利用短路运算符 || 和 && 替代简单的if语句||返回遇到的第一个真值,&&返回遇到的第一个假值12(A || B) && fun() // 相当于 if(A || B) fun()A && B && fun() // 相当于 if(A && B) fun() 数组中取最大/小值12let min = Math.min(...arr)let max = Math.max(...arr) 创建一个可迭代对象123456789101112131415161718var foo = { 0 : '00', 1 : '11', 2 : '22', 3 : '33', length : 4}// 使对象可迭代 如果没有这一步,会报 object is not iterable 的错foo[Symbol.iterator] = function() { let i = 0, that = this return { next() { return i < that.length ? { done: false, value: that[i++] } : { done: true } } }}// Set函数接受一个具有 iterable 接口数据结构,否则会报错new Set(foo) // Set(4) {"00", "11", "22", "33"} 使 a === a - 1 为true使用 Infinity 可以做到,但是怎么才能得到正负Infinity的值?方法一:数值运算的值,超过了Number允许表示的范围;方法二:将一个不为0的正负数除以01234567891011121314// 无穷小的数a = -Infinityconsole.log(a === a - 1) // true// 无穷大的数a = Infinityconsole.log(a === a - 1) // true// 无穷数的运算Infinity + Infinity // InfinityInfinity - Infinity // NaNInfinity * Infinity // InfinityInfinity / Infinity // NaNInfinity * 0 // NaN 使 a == 1 && a == 2 && a == 3 返回 true在引擎读取 a 的值时,在方法内部做处理实现宽松相等 ==12345678910111213// 方法一:使用 toString 或者 valueOfa = { i: 1, toString: () => a.i++, valueOf: () => a.i++,}// 方法二:使用 Proxy a = new Proxy({ i: 1 }, { get(obj) { return () => obj.i++ }}) console.log(a == 1 && a == 2 && a == 3) // true 实现严格相等 ===12345678910111213141516171819// 方法一:i = 1Object.defineProperty(window, 'a', { get: () => i++ })// 方法二:value = function* () { let i = 1 while(true) yield i++}()Object.defineProperty(window, 'a', { get() { return value.next().value }})console.log(a === 1 && a === 2 && a === 3) // true 使 a == b && a == c && c != b 为 true代码实现1234567891011121314151617181920let a,b,c// 方法一:利用复杂类型指向引用的特性,将 b,c 设置为复杂类型// "[]" != []a = false, b = [], c = []console.log( '结果--array', a == b && a == c && c != b ) // true// "{}" != {}a = "[object Object]", b = {}, c = {}console.log( '结果--object', a == b && a == c && c != b ) // true// "[object Function]" != function(){}a = "function(){}", b = function(){}, c = function(){}console.log( '结果--function', a == b && a == c && c != b ) // true// 方法二:利用proxy劫持b = 1, c = 2a = new Proxy({ i: 1 }, { get(obj) { return () => obj.i++ } })console.log( '结果--proxy', a == b && a == c && c != b ) // true 为什么[“1”, “2”, “3”].map(parseInt) 返回值是[1, NaN, NaN]map()调用callback函数时,会给它传递三个参数:当前正在遍历的元素、元素索引、原数组本身parseInt接受两个参数:元素、进制数遍历时parseInt的第二个参数被传进来的是元素的索引值,所以…12345["1", "2", "3"].map(parseInt) // [1, NaN, NaN]["1", "2", "3"].map(item => parseInt(item))// [1, 2, 3] 利用 a 标签解析 URL12345678910111213141516171819202122function parseURL(url) { let a = document.createElement('a') a.href = url return { host: a.hostname, port: a.port, query: a.search, params: (function(){ var result = {}, str = a.search.replace(/^\?/,'').split('&') for (let i in str) { let s = str[i].split('=') result[s[0]] = s[1] } return result })(), hash: a.hash.replace('#','') }}parseURL('https://www.google.com:8008?a=1&b=2')// 其他快速url参数的方法new URLSearchParams(location.search).get("a") // 1 删除链接中某个参数URLSearchParams MDN123456789101112131415161718192021222324252627282930// 利用URLSearchParamsfunction delSearchParam(name, search = location.search) { let _params = new URLSearchParams(search.substr(1)) _params.delete(name) return `?${_params.toString()} `}// 普通的function delSearchParam(name, search = location.search) { let _arr = search.split("?")[1].split("&") // 参数变成数组 let _map = {} for(let i = 0; i < _arr.length; i++) { let _t = _arr[i].split("=") _map[_t[0]] = _t[1] } // 删除这个参数 if(_map[name]) { delete _map[name] } // 把删除后的数组变成字符串 let _result = '?' for(let i in _map) { _result += `${i}=${_map[i]}&` } // 删除字符串最后一个元素 return _result.substr(0, _result.length-1)}delSearchParam('a', '?a=1&b=2&c=3') 实现吸顶参考文章–交叉观察者MDN–IntersectionObserver方法一效果1234.target { position: sticky; top: 0;} 使用条件: 父元素不能overflow:hidden或者overflow:auto属性 必须指定top、bottom、left、right4 个值之一,否则只会处于相对定位 父元素的高度不能低于sticky元素的高度 sticky元素仅在其父元素内生效 方法二效果123456789101112131415var a = document.querySelector('.a')var b = document.querySelector('.b')new IntersectionObserver(function(e) { let offsetTop = a.getBoundingClientRect().top if(offsetTop < 0) { a.style.position = 'fixed' a.style.top = 0 a.style.left = '50%' a.style.transform = 'translateX(-50%)' } else { a.style.position = 'relative' }}, { threshold: [1]}).observe(a) 时间相关的计算1234567891011// 计算当前日期天数let dayOfYear = date => Math.floor((date - new Date(date.getFullYear(), 0, 0)) / (1000 * 3600 * 24))dayOfYear(new Date()) // 301// 时间戳转日期时间let getDate = time => new Date(time).toLocaleDateString().replace(/\//g, "-") + " " + new Date(time).toTimeString().substr(0, 8)getDate(Date.now()) // "2019-10-28 17:36:47" 实现深拷贝代码实现 参考文章 Map MDNPS:扩展符...可实现数组的深拷贝123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114// Step 1:深拷贝数组和对象function clone(target) { if(typeof target == 'object') { let _target = Array.isArray(target) ? [] : {} for(let i in target) { _target[i] = clone(target[i]) } return _target } else { return target }}// Step 2:但是如果循环引用,上面的代码就很会导致栈内存溢出// 【解决】利用Map,拷贝前检查有无拷贝过 // => Map是强引用类型,内存不会自动释放,会造成非常大的额外消耗(任何值都可以作为一个键或一个值)// => 使用WeakMap弱引用类型,内存会自动释放(键必须是对象,而值可以是任意的)function clone(target, map = new WeakMap()) { if (typeof target === 'object') { let _target = Array.isArray(target) ? [] : {} if(map.get(target)) { return map.get(target) } map.set(target, _target) for(let i in target) { _target[i] = clone(target[i], map) } return _target } else { return target }}// Step 3:做一下性能优化// 【优化点】执行效率:while、for > for in// 其中for in执行效率是三者中最差的,所以不能用for inif(false) { let arr = [...new Array(1000000).keys()] console.time() // 第 1 种:while 用时:8.023193359375ms let i = 0 while(i < arr.length) { i++ } // 第 2 种:for 用时:7.807861328125ms for(let i = 0; i < arr.length; i++) { } // 第 3 种:for in 用时:190.059814453125ms for(let i in arr) { } // 第 4 种:for in 用时:10.40576171875ms arr.forEach(() => {}) console.timeEnd()}function clone(target, map = new WeakMap()) { if (typeof target === 'object') { let isArray = Array.isArray(target); let _target = isArray ? [] : {} if(map.get(target)) { return map.get(target) } map.set(target, _target) let keys = isArray ? undefined : Object.keys(target) forEach(keys || target, (value, i) => { if (keys) { i = value } _target[i] = clone(target[i], map) }) return _target } else { return target }}function forEach(array, iteratee) { let index = -1; const length = array.length; while (++index < length) { iteratee(array[index], index); } return array;}// Step 4:兼容其他数据类型// 以上代码只兼容object和array两种数据类型,值如果存在null、function则会报错// 一个简单的测试let target = { a: undefined, b: 'hello', c: { child: 'child' }, d: [1, 2, 3], f: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: {} } } } } } } } } } } },}let result = clone(target)target.target = target // 循环引用target.c.child = 'new child' // 修改原对象console.log(result) // 打印拷贝值 防抖防抖查看效果:地址12345678910// 触发间隔超过指定间隔的任务才会执行,连续发生动作会刷新时间function debounce(fn, delay, ...args) { let timer = null return function () { clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, args) }, delay) }} 节流节流查看效果:地址123456789101112131415161718192021222324// 在指定间隔内任务只执行一次,连续发生的动作会被忽略function throttle(fn, delay, ...args) { let prev = Date.now() return function () { let now = Date.now() if (now - prev >= delay) { fn.apply(this, args) prev = Date.now() } }}function throttle(fn, delay, ...args) { let timer return function () { if (!timer) { timer = null timer = setTimeout(() => { timer = null fn.apply(this, args) }, delay) } }}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JS原理性知识的函数实现]]></title>
<url>%2F2019%2F07%2F10%2FJS%E5%8E%9F%E7%90%86%E5%AE%9E%E7%8E%B0%2F</url>
<content type="text"><![CDATA[JS一些原理性的知识用函数自己实现 new123456789101112131415161718const People = function(name) { this.name = name}People.prototype.sayName = function() { console.log('my name is ' + this.name)}// step1:创建一个新对象 obj// step2:把 obj 的 __proto__ 指向 People.prototype 实现继承// step3:执行构造函数,传递参数,改变this指向 People.call(obj, ...args)// step4:结果是 null 和 undefined 时不处理function _new(fn, ...arg) { let obj = Object.create(fn.prototype) let result = fn.call(obj, ...arg) return result instanceof Object ? result : obj}let my = _new(People, 'Nola') 双向绑定查看效果:地址12345678910111213141516171819202122232425262728293031var input = document.getElementById('input')var show = document.getElementById('show')input.addEventListener('input', function(e) { obj.text = e.target.value})// defineProperty 实现var obj = {}Object.defineProperty(obj, 'text', { configurable: true, enumerable: true, get: function() { return obj.text }, set: function(newValue) { input.value = newValue show.innerText = newValue }})// proxy 实现var obj = new Proxy({}, { get: function(obj, prop) { return obj[prop] }, set: function(obj, prop, newValue) { obj[prop] = newValue input.value = newValue show.innerText = newValue }}) call1234567Function.prototype.call = function(context, ...args) { context || (context = window) context.fn = this let result = eval(`context.fn(${args})`) delete context.fn return result} apply1234567Function.prototype.apply = function(context, args) { context || (context = window) context.fn = this let result = eval(`context.fn(${args})`) delete context.fn return result} bind123456Function.prototype.bind = function(context, args) { if(typeof this !== 'function') return context || (context = window) let result = arg => this.apply(context, args.concat(arg)) return result}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Scrapy入门]]></title>
<url>%2F2019%2F07%2F08%2FScrapy%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[记录Scrapy入门的过程及中间遇到的一些坑。最近在爬网易云热评(对于获取歌曲列表的反爬还没做处理,目前只能一个歌单一个歌单爬),为了方便查看,顺便写了个网页专门用来展示网易云热评(热评质量参差不齐,以后再慢慢做数据筛选叭)。热评地址:https://nolaaaaa.github.io/demo/scrapy-wangyiyun/index。 新建scrapy项目 test1$ scrapy startproject test 生成爬虫文件 test_spider.py从终端进入test项目中的spiders文件夹,执行:1$ scrapy genspider test_spider test.com # 最后两个参数:文件名 + 域名 明确要抓取的目标 items.py在items.py里面编写,格式如:name = scrapy.Field()12class TestItem(scrapy.Item): name = scrapy.Field() 编写爬虫文件12345678910class TestSpierSpider(scrapy.Spider): # 爬虫名字 name = 'test_spider' # 允许的域名 allowed_domains = ['test.com'] # 入口url,引擎会将url给调度器处理,调度器调度后又把请求给引擎,引擎把请求交给下载器下载解析 start_urls = ['https://test.com'] # 请求返回的数据解析 def parse(self, response): print (response.text) 运行爬虫文件1$ scrapy crawl test_spider 报错:DEBUG: Crawled (403) <GET https://test.com> (referer: None)处理:进入settings.py文件,找到USER_AGENT这个参数,从目标网站的请求头中复制User-Agent的值给USER_AGENT,再重新运行下爬虫文件即可 爬虫文件运行成功后即可看到response返回的html数据 运行爬虫文件更加简单的办法,test项目下新建一个文件main.py,再次运行爬虫文件无需进终端,运行此文件即可12from scrapy import cmdlinecmdline.execute('scrapy crawl test_spider'.splite()) 目前无法正常执行,寻找原因ing 爬虫文件中写xpath解析内容可利用谷歌插件XPath helper编写xpath用xpath解析完了之后,需将数据yield到pipelines里面去才能正常导出数据 保存数据为JSON、CSV格式导出数据存储为json文件:1$ scrapy crawl test_spider -o test.json 也可存为csv格式,后缀改一下就好了 保存数据到数据库 pipelines.py第一步:在pipelines文件里进行编辑第二步:settings里启用 ITEM_PIPELINES 选项第三步:开启mongod服务:mongod,客户端建立连接第四步:运行爬虫文件$ scrapy crawl test_spider第五步:查看保存内容:客户端shell中输入:db.test.find().pretty() PS:关闭mongo服务1234567891011# 第一种:正常关闭> use admin> db.shutdownServer()# 第二种:启动报错时$ lsof -i :27017 # 查看端口占用情况,可获取开启的进程号(PID) $ ps -ef | grep mongo # 查看当前进程$ kill -2 PID号 # 关闭对应的进程,其中 -9 不安全,可使用 -2、-4# 修复mongod$ mongod --repair 爬虫伪装–IP、User-Agent中间件编写 middlewares.py设置代理IP、随机User-Agent目的:有效隐藏爬虫地址,防止被对方发现第一步:在middlewares文件里进行编辑第二步:settings里启用 DOWNLOADER_MIDDLEWARES 选项,其中类名与middlewares中编辑配置代理IP的类名一致,注意:User-Agent和IP中间件的优先级不能相同第三步:运行爬虫文件$ scrapy crawl test_spider第四步:查看运行结果:如果有Enabled downloader middlewares字段则表示使用代理成功]]></content>
<categories>
<category>后端</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[安装 Scrapy 遇到的一些坑及处理]]></title>
<url>%2F2019%2F07%2F07%2F%E5%AE%89%E8%A3%85Scrapy%E7%9A%84%E8%BF%87%E7%A8%8B%2F</url>
<content type="text"><![CDATA[记录了安装 Scrapy 的过程,及遇到的一些坑的处理 使用的电脑及 Python 版本电脑:Mac,Python的版本:3.7.3 下载 Scrapy在终端输入如下命令:1$ python3 -m pip install scrapy 遇到报错:Building wheel for Twisted ( [setup.py](setup.py) ) ... error解决办法:下载 Twisted 下载 Twisted下载地址:Twisted下载 tar.bz2 格式文件后>>解压>>进入解压后的文件夹终>>运行如下命令进行安装:1$ python3 setup.py install 遇到报错:error: command ‘gcc’ failed with exit status 1解决办法:使用 brew 重新下载 Python3 ,下载完成后重新执行第2步]]></content>
<categories>
<category>后端</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript入门]]></title>
<url>%2F2019%2F05%2F24%2FTypeScript%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[学习TypeScript入门教程时的笔记和总结。 基本类型boolean:let temp: boolean = false。注意:new Boolean(1)返回的是Boolean 对象,Boolean(1)返回的是boolean 类型number:Ts中的16进制转化为Js的时候还是16进制,但是2、8进制会转化成10进制string:可用模板字符串void:空值,可以用 void 表示没有任何返回值的函数,声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 nullnull 和 undefined:与 void 的区别是,undefined 和 null 是所有类型的子类型,可以赋值给 number 类型的变量,而 void 不可以any:任意值,允许被赋值为任意类型,对它的任何操作,返回的内容的类型也都是任意值。变量声明时,未指定其类型,且定义时未赋值,则默认为 any 类型。 类型推论:如果定义时未指定其类型,且赋值,则会根据定义时所赋值的类型来推断其类型。PS:如果定义时未指定其类型,且未赋值,则默认为 any类型。 字符串字面量类型用来约束取值只能是某几个字符串中的一个 使用 type 进行定义,type EventNames = 'click' | 'scroll' | 'mousemove' 联合类型:表示取值可以为多种类型中的一种。使用 | 分隔每个类型,如:let temp: string | number 当联合类型的变量没有被赋值时,只能访问此联合类型的所有类型里共有的属性或方法。(当联合类型的变量被赋值时,会根据类型推论的规则推断出一个类型。) 对于第1点,变量未被赋值时只能访问共有属性,解决办法是:使用时先 类型断言 某个变量为特定类型(联合类型中定义过的),如:(<boolean>temp).length(格式:<类型>值或值 as 类型(tsx 语法中)) 枚举类型用于取值被限定在一定范围内的场景 使用 enum 进行定义,如:enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat} 枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射,如:Days["Sun"] === 0 // true Days[0] === "Sun" // true 手动给部分枚举项赋值时,未手动赋值的部分会自动编译,可能会有覆盖的情况 手动赋值的枚举项不是数字时,需使用类型断言让 tsc 无视类型检查:Sat = <any>"S" 手动赋值的枚举项是小数或负数时,此时后续未手动赋值的项的递增步长仍为 1 枚举项有两种类型:常数项和计算所得项(后面若是未手动赋值的项,会因无法获得初始值而报错) 常数枚举:使用 const enum 定义,会在编译阶段被删除,并且不能包含计算成员 外部枚举:使用 declare enum 定义,declare定义的类型只会用于编译时的检查,编译结果中会被删除,常出现在声明文件中,也可同时使用 declare 和 const,如:declare const enum Temp { ... } TypeScript 的枚举类型的概念来源于 C# 对象类型——接口接口(Interfaces)用于定义对象的类型。 赋值时,接口和变量的形状必须一致,1V1,多一个少一个都不行 对于第1点:当一个属性被定义为可选属性,即可以不给这个属性赋值 不允许添加未定义的属性 对于第2点:当接口定义了任意属性,则可以添加未提前定义的属性1234567891011interface Person { readonly id: number; // readonly 定义只读属性,初始化后不能再被赋值 name: string; // 必须要赋值 age?: number; // ?: 定义可选属性,可以不赋值 [propName: string]: any; // 定义任意属性}// Person 是 nola 的接口let nola: Person = { id: 249399; name: 'Nola';}; 内置对象类型 ECMAScript 的内置对象:Boolean、Error、Date、RegExp 等 DOM 和 BOM 的内置对象:Document、HTMLElement、Event、NodeList 等 TypeScript 核心库的定义文件中定义了所有浏览器环境需要用到的类型,如使用方法Math.pow(10, '2')时会报错,因为Math.pow 必须接受两个 number 类型的参数 TypeScript 核心库的定义中不包含 Node.js部分1234567let b: Boolean = new Boolean(1);let e: Error = new Error('Error occurred');let d: Date = new Date();let r: RegExp = /[a-z]/;let body: HTMLElement = document.body;let allDiv: NodeList = document.querySelectorAll('div');document.addEventListener('click', function(e: MouseEvent) {}); 数组类型定义数组类型的几种方法: 类型 + []:let temp: number[] = [1, 2, 3],注意:数组中值的类型需和[]前的类型一致 Array<number>数组泛型:let temp: Array<number> = [1, 2, 3],注意:数组中值的类型需和<...>中定义的类型一致 使用接口描述数组:interface NumberArray { [index: number]: number }PS:类数组不是数组,不能用数组的方式定义,可用类数组自己的接口定义,如 IArguments、NodeList、HTMLCollection 元组类型数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。 定义:let Nola: [string, number] 赋值:Nola[0] = 'Nola'或Nola=['Nola',25] 使用Nola.push('')添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型 函数类型函数的类型定义:1234567891011// ts 函数声明的类型定义function sum(x: number, y: number): number { return x + y }// ts 函数表达式的类型定义let sum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y }// 使用接口定义函数需要的形状interface Func { (x: string, y: string): boolean; interval: number; // 定义函数自己的属性 reset(): void; // 定义函数自己的方法}let sum: Func 使用注意: 使用时的参数不能 少于/多于 所定义的参数数量 对于第1点:可使用 ?: 定义可选参数,但选参数后面不允许再出现必须参数 TypeScript 中的 =>:(输入类型) => 输出类型 参数默认值:name: string = 'Nola',ts默认该参数为可选参数,但是后面也可出现必须参数 剩余参数 ...rest 是一个数组,可用 ...items: any[] 方式定义。注意; ...rest 参数只能是最后一个参数 重载:允许一个函数接受不同数量或类型的参数时,作出不同的处理 类类的用法具体参考:TypeScript入门教程 - 类和ECMAScript 6 入门 - Class (ES6)类的继承:使用 extends 实现继承,如:class A extends B,子类中使用 super 调用父类的构造函数和方法 (ES6)静态方法:使用 static 修饰,如:static tamp(a),它们不需要实例化,而是直接通过类来调用 (ES7)静态属性:使用 static 修饰,如:static age = 24 (TS)访问修饰符:public(公有的,值可以被外部获取和修改,默认)、private(私有的,无法直接存取,子类中也不允许被访问) 和 protected(和 private 类似,区别是它在子类中允许被访问) (TS)抽象类:使用 abstract 定义,如:abstract class A,不允许被实例化,可以被继承,但抽象类中的抽象方法必须被子类实现,编译结果中,会存在这个类 类与接口接口的另一个用途,对类的一部分行为进行抽象 类的特性提取成接口:使用 implements 实现,如:class A implements C(C使用 interface C 定义),多个接口之间可使用,隔开 接口继承接口:interface C extends D(D使用 interface D 定义),接口也可以继承类 泛型在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性123456// T、U 用来指代任意输入的类型function foo<T>(value: T): Array<T> {...} function foo<T, U>(value: [T, U]): [U, T] {...} // 泛型约束,extends 约束了泛型 T 必须符合对应的形状function foo<T extends Temp>(value: T): T {...} // Temp为自定义的一个接口function foo<T extends U, U>(value: T, source: U): T // 多个类型参数之间也可以互相约束 泛型中的类型参数指定默认类型,如:<T = string> 泛型可用于函数、类的类型定义中,如:interface Temp<T> {} class Temp<T> {} 声明合并如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型 函数的合并:使用重载定义多个函数类型 接口合并:两个相同名字的属性会合并到一个接口中,但是合并的属性的类型必须是唯一的(如两个属性相同,类型相同不会报错,不同时会报错) 类的合并与接口的合并规则一致 类型别名类型别名常用于联合类型 使用 type 创建类型别名 声明文件内容太多了,简单总结一下,具体该教程地址第三方声明文件使用 @types 统一管理第三方库的声明文件,如:npm install @types/name --save-dev 全局变量的声明文件通过 <script> 标签引入第三方库 声明文件必需以 .d.ts 为后缀 声明语句只能定义类型,不能在声明语句中定义具体的实现,declare let Name: (selector: string) => any; 全局变量的声明文件的几种语法: declare var 声明全局变量 declare function 声明全局方法 declare class 声明全局类 declare enum 声明全局枚举类型 declare namespace 声明(含有子属性的)全局对象 interface 和 type 声明全局类型 为防止命名冲突,应尽可能的减少全局变量或全局类型的数量,最好将他们放到 declare namespace 下 npm 包的声明文件通过 import foo from 'foo' 导入 创建声明文件前先看看它的声明文件是否已经存在 npm 包声明文件存在的地方: 第一:与该 npm 包绑定在一起。判断依据是 package.json 中有 types 字段,或者有一个 index.d.ts 声明文件 第二:@types 里,尝试安装一下对应的 @types 包就知道是否存在该声明文件 创建 npm 包声明文件的方法: 第一种:创建一个 node_modules/@types/foo/index.d.ts 文件,存放 foo 模块的声明文件(有风险,如无法版本回溯或可能被删掉,不推荐) 第二种:创建一个 types 目录,专门用来管理自己写的声明文件,将 foo 的声明文件放到 types/foo/index.d.ts 中。这种方式需要配置下 tsconfig.json 中的 paths 和 baseUrl 字段 npm 包的声明文件的几种语法: export 导出变量(如:export const/function/class/enum/interface,export namespace 导出(含有子属性的)对象,也可以用declare声明多个变量(interface 不用加 declare),再用export导出 export { name、age }) export default ES6 默认导出(导入时用import foo from 'foo' 而不是 import { foo } from 'foo',只有 function、class 和 interface 可以直接默认导出,其他的变量需要先定义出来,再默认导出,如:export default enum 是错误的语法,需要使用 declare enum 先定义)、commonjs 规范的导出 export = xxx(导入方法:const ... = require('...')/import ... from/import ... = require('...')) UMD 库的声明文件可以通过 <script> 标签引入,又可以通过 import 导入 基于 npm 包的类型声明文件,需要额外声明一个全局变量,语法为 export as namespace export as namespace可与export default一起使用 改变全局变量的类型 直接扩展全局变量:通过 <script> 标签引入后,改变一个全局变量的结构,通过声明合并(如扩展String可使用interface String)、使用 declare namespace 给已有的命名空间添加类型声明在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构,语法为:declare global 模块插件:通过 <script> 或 import 导入后,改变另一个模块的结构,语法为:declare module 自动生成声明文件如果库的源码本身就是由 ts 写的,那么在使用 tsc 脚本将 ts 编译为 js 的时候,添加 declaration 选项,就可以同时也生成 .d.ts 声明文件 在命令行中添加 --declaration(简写 -d) tsconfig.json 中添加 declaration 选项,"declaration": true 发布声明文件 将声明文件和源码放在一起 将声明文件发布到 @types 下]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>TypeScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python--函数式编程、模块和包、面向对象编程]]></title>
<url>%2F2019%2F04%2F23%2FPython-%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B%E3%80%81%E6%A8%A1%E5%9D%97%E5%92%8C%E5%8C%85%E3%80%81%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%2F</url>
<content type="text"><![CDATA[Python–函数式编程、模块和包、面向对象编程 1 函数式编程高阶函数高阶函数:能接收一个函数作为参数的函数。Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数。 Python 内置的高阶函数: map(f, list) 把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。123def f(x): return x*xprint map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) reduce(f, list) reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。reduce()还可以接收第3个可选参数,作为计算的初始值。1234# 求一个list的积:def prod(x, y): return x*yprint reduce(prod, [2, 4, 5, 7, 12]) filter(f, list) 函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。123456789101112# 利用filter()过滤出1~100中平方根是整数的数:import mathdef is_sqr(x): temp = int(math.sqrt(x)) if temp*temp==x: return xprint filter(is_sqr, range(1, 101))# s.strip(rm) 删除 s 字符串中开头、结尾处的 rm 序列的字符。当rm为空时,默认删除空白符(包括'\n', '\r', '\t', ' '):a = ' 123'a.strip()# '123' sorted(list, f) 函数可对list进行排序,可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。12345678910111213sorted([36, 5, 12, 9, 21])# [5, 9, 12, 21, 36]# sorted() 函数实现倒叙排序:def reversed_cmp(x, y): if x > y: return -1 if x < y: return 1 return 0 sorted([36, 5, 12, 9, 21], reversed_cmp)# [36, 21, 12, 9, 5] sort 与 sorted 区别:sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。 闭包闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。12345678910111213141516171819202122def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()# 9 9 9# 以上代码期望输出的是 1 4 9,但最终结果却是 9 9 9 。改动代码达到期望效果:def count(): fs = [] for i in range(1, 4): def f(i = i): return i*i fs.append(f) return fs f1, f2, f3 = count()# 1 4 9 匿名函数匿名函数 lambda x: x * x 实际上就是:123def f(x): return x * x# 关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数。 ps:匿名函数有个限制,就是只能有一个表达式,不写 return,返回值就是该表达式的结果。 装饰器目的:简化代码,避免每个函数编写重复的代码。1234567891011121314151617181920212223242526272829303132333435363738# 编写一个无参数的@performance,它可以打印出函数调用的时间:import timedef performance(f): def wrapper(*args, **kw): t1 = time.time() res = f(*args, **kw) t2 = time.time() print 'call %s() in %s' %(f.__name__, (t2 - t1)) return res return wrapper@performancedef factorial(n): return reduce(lambda x,y: x*y, range(1, n+1))print factorial(10)# 给 @performace 增加一个参数,允许传入's'或'ms':import timedef performance(unit): def log_decorator(f): def wrapper(*args,**kw): t1 = time.time() res = f(*args, **kw) t2 = time.time() t = (t2 - t1)*1000 if unit =='ms' else (t2 - t1) print 'call %s() in %s %s'%(f.__name__, t, unit) return res return wrapper return log_decorator@performance('ms')def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1))print factorial(10) @decorator 可以动态实现函数功能的增加,但是,经过@decorator “改造”后的函数的函数名等属性也会被改造,变成改造器内部定义的wrapper,我们也很难把原函数的所有必要属性都一个一个复制到新函数上,所以Python内置的functools可以用来自动化完成这个“复制”的任务:在wrapper函数前使用`@functools.wraps(f)`[email protected](f) def wrapper(*args, **kw): # do something 偏函数functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。 2 模块和包包是文件夹,模块是.py文件,每一层包下面必须有一个init.py文件,即使包下面没有其他文件,因为只有这样,python才会把该文件夹当成包来处理。目的:避免同一个名字的变量互相影响 导入模块123456789101112131415161718192021222324# import... 文件中导入模块:import math# math.pow(2,3)# from...import... 导入用到的math模块的某几个函数,而不是所有函数:from math import pow, sin, log# pow(2,3)# from...import...as... 解决不同模块的命名冲突:from math import pow as poww# poww(2,3)# 两个不同的模块中相同功能的 StringIO 函数,没有模块 cStringIO ,则会从 StringIO 模块中导入 StringIO 函数: try: from cStringIO import StringIOexcept ImportError: from StringIO import StringIO# 两个不同名称功能相同的模块,没有模块 json ,则会导入 simplejson ,且重命名为 json :try: import json as jsonexcept ImportError: import simplejson as json 使用新版本的特性当新版本的一个特性与旧版本不兼容时,该特性将会在旧版本中添加到future中,以便旧的代码能在旧版本中测试新特性。1234from __future__ import #新规则名称# 在Python 3.x中,字符串统一为unicode,不需要加前缀 u,而以字节存储的str则必须加前缀 b,__future__的unicode_literal可以兼容from __future__ import unicode_literals 模块管理工具模块管理工具:easy_install、pip or pip3(Python 推荐,已内置到Python2.7.9)查找第三发模块的名字:https://pypi.python.org/ 3 面向对象编程类和实例类用于定义抽象类型(class Person(object):)实例根据类的定义被创建出来(nola = Person()) 定义属性设置类的属性:类属性是直接绑定在类上的,所以,访问类属性不需要创建实例,就可以直接访问。 设置实例的属性:在init() 里除了可以直接使用self.name = ‘xxx’设置一个属性外,还可以通过 setattr(self, ‘name’, ‘xxx’) 设置属性。一个实例的私有属性就是以__开头的属性,无法被外部访问,但是,从类的内部是可以访问的。 1234567891011121314class Person(object): # 设置类的属性 count = 0 def __init__(self, name, age): # 设置实例的属性 self.name = name setattr(self, 'age', age) Person.count += 1p = Person('Nola', 24)print Person.countprint p.name print p.age ps:如果一个属性由双下划线开头”“,该属性就无法被外部访问;如果一个属性以”xxx__”的形式定义。类属性可以动态添加和修改。ps:当实例属性和类属性重名时,实例属性优先级高。 定义方法定义类方法:通过标记一个 @classmethod,该方法将绑定到 Person 类上,而不是类的实例上。类方法的第一个参数将传入类本身,通常将参数名命名为 cls。调用方式:Person.how_many()。123456789101112class Person(object): __count = 0 # 定义一个类属性,由于双下划线开头,只有类中可以访问到,实例上访问不到 @classmethod # 定义一个类方法,类可以调用,可以用于获取类中的属性 def how_many(cls): return cls.__count # cls.count 实际上相当于 Person.count def __init__(self, name): self.name = name Person.__count = Person.__count + 1 print Person.how_many()p = Person('Bob')print Person.how_many() 定义实例方法:python中方法也是属性,调用方法和属性一样。在class中定义的实例方法第一个参数 self 是实例本身。同上一个例子,去掉 @classmethod 标记,传入表示实例的参数 self, how_many(self) 则变成实例方法。调用方式:p.how_many()。12345678910111213141516171819# coding = utf-8class Person(object): def __init__(self, name, score): self._score = score # 实例方法, 第一个参数 self 是实例 def get_grade(self): if self._score >= 80: return 'A-优秀' if self._score >= 60: return 'B-及格' if self._score < 60: return 'C-不及格'p = Person('Bob', 90)print p1.get_grade()# 由于例子中有中文,所以需要在代码开始定义 # coding=utf-8 给一个实例动态添加方法,用 types.MethodType() 把一个函数变为一个方法(types需要先import):123456789101112131415import typesdef fn_get_grade(self): if self.score >= 80: return 'A' if self.score >= 60: return 'B' return 'C' class Person(object): def __init__(self, name, score): self.name = name self.score = score p = Person('Bob', 90)p.get_grade = types.MethodType(fn_get_grade, p, Person) 类的继承123456789101112# Person 类继承自 objectclass Person(object): def __init__(self, name, gender): self.name = name self.gender = gender# Student 类继承自 Personclass Student(Person): def __init__(self, name, gender, score): # 继承需要调用 super(class, self).__init__(args) 初始化父类 super(Student, self).__init__(name, gender) self.score = score 获取对象信息isinstance() 可以判断一个变量的类型,既可以用在Python内置的数据类型如str、list、dict,也可以用在我们自定义的类,它们本质上都是数据类型。1isinstance(p, Person) # (实例,原型)返回布尔值 type() 函数获取变量的类型,返回一个 Type 对象:12type(123)# <type 'int'> dir() 函数获取变量的所有属性 getattr() 和 setattr() 函数获取或者设置对象的属性:123getattr(s, 'name') # 获取name属性'Bob' setattr(s, 'name', 'Adam') # 设置新的name属性为 'Adam' 定制类–特殊方法在Python中,函数其实是一个对象,所有的函数都是可调用对象。call(self, 调用实例时传入的参数):将一个类实例变成一个可调用对象 slots:限制当前类所能拥有的属性,如果不需要添加任意动态的属性,使用slots也能节省内存1234567class Student(object): # 只能有 'name', 'gender', 'score' 三个属性 __slots__ = ('name', 'gender', 'score') def __init__(self, name, gender, score): self.name = name self.gender = gender self.score = score str():把一个类的实例变成 str。用于显示给用户(print 出来的:print p)repr():用于显示给开发人员(直接调用的:p)12# 让 __repr__ 和 __str__ 一样__repr__ = __str__ 整理自Python进阶]]></content>
<categories>
<category>后端</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python--list、tuple、dict、set]]></title>
<url>%2F2019%2F04%2F18%2FPython-list%E3%80%81tuple%E3%80%81dict%E3%80%81set%2F</url>
<content type="text"><![CDATA[Python–list、tuple、dict、set,新增:字符串及相关方法 + 序列相关函数和方法 查看类型:type(i)数值类型:int, float, complex, bool容器:不可变容器:str, tuple frozenset, bytes,可变的容器:list, dict, set, bytearray值:None, False, True序列相关函数:序列有5种:str, list, tuple, bytes, bytearray| 名称 | 用途 ||———–|———–|———–|| len(L) | 序列长度 || max(L) | 最大值元素 || min(L) | 最小值元素 || sum(L) | 所有元素的和 || any(L) | 只要有值为真则返回 True || all(L) | 所有值为真则返回 True || reversed(L) | 返回反向顺序的可迭代对象 || sorted(L) | 返回已排序的列表 | range()函数:1234567range(4) # 0, 1, 2, 3range(0, 4) # 0, 1, 2, 3range(0, 4, 1) # 0, 1, 2, 3range(0, 4, 2) # 0, 2range(4, 0, -2) # 4, 2range(4, 0, -1) # 4, 3, 2, 1range(4, 0) # 空 字符串创建:字符串构造函数:str(i)(字符串转数字int(i))原始字符串:r'字符串内容'让\无效运算:12345# * 乘:用于生成重复的字符春"123" * 3 # '123123123' # + 加:用于拼接"123" * "456" # '123456' 切片:语法:[开始索引:结束索引:步长],步长为正数时正向切片,步长为负数时反向切片,默认最后一个元素开始,第一元素的前一个位置结束123456789# 字符: A B C D E# 正向: 0 1 2 3 4# 反向:-5 -4 -3 -2 -1s = "ABCDE"print(s[1:4]) # BCDprint(s[1:4:2]) # BDprint(s[::-1]) # EDCBA 编码转换函数:| 名称 | 用途 ||———–|———–|———–|| ord(i) | 返回字符i的Unicode值 || chr(i) | 返回整数i(Unicode)对应的字符 | 整数转字符串函数:| 名称 | 用途 ||———–|———–|———–|| bin(i) | 整数转二进制字符串 || oct(i) | 整数转八进制字符串 || hex(i) | 整数转十六进制字符串 | 常用的字符串方法:| 名称 | 用途 ||———–|———–|———–|| S.isalpha() | 是否全是英文字符 || S.isdigit() | 是否全是数字字符 || S.isspace() | 是否全是空白字符 || S.islower() | 字母是否全是小写 || S.isupper() | 字母是否全是大写 || S.istitle() | 是否是首字母大写 || S.center(width) | 根据传入的width,将S居中,左右默认填充空格 || S.count(sub, start, end) | 获取sub在S中的个数,第2、3个参数指定查找位置 || S.find(sub, start, end) | 获取sub在S中的索引,失败返回-1 || S.strip() | 去除所有空格 || S.lstrip() | 去除左侧空格 || S.rstrip() | 去除右侧空格 || S.upper() | 英文转大写 || S.lower() | 英文转小写 || S.replace(old, new, count) | 将old替换成new,count为替换的个数 || S.startswith(prefix, start, end) | S是否是prefix开头的 || S.endswith(suffix, start, end) | S是否是suffix结尾的 || S.split(sub) | 根据sub分割S成列表,默认为’ ‘ || ‘sep’.join(seq) | 连接字符串数组,@sep:分隔符 @seq:列表、字符串、字典 | 格式化字符串中的占位符和类型码:%[- + 0 宽度.精度],其中:-左对齐,+显示正号,0左侧空白位置补零,精度默认6位| 名称 | 用途 ||———–|———–|———–|| %s | 字符串,使用str(obj)转为字符串 || %r | 字符串,使用repr(obj)转为字符串 || %c | 整数转为字符串,使用chr(i)函数 || %d | 十进制整数 || %o | 八进制整数 || %x | 十六进制整数(小写) || %X | 十六进制整数(大写) || %e | 指数型浮点数(E小写) || %E | 指数型浮点数(E大写) || %f, %F | 浮点十进制 || %g, %G | 浮点、指数形式自动转化 || %% | 等同%字符,如50% | 列表 list创建:构造函数:list(),可用 [] 创建取值:根据索引,如 L[0] 取出索引为 0 的值;切片赋值:能修改、添加、删除列表中的元素,如:L[0:3] 取出从索引 0-3 的值,其中不包括索引 3 的值(删除:del 列表[索引 || 切片] 删除列表中的元素)特点:有序集合。list 能修改。查找速度随元素的增加而变慢。内存占用小列表推导式:[ 表达式 for 变量 in 可迭代对象 if 真值表达式 ],if子句可省略123456789101112131415161718192021222324252627282930313233343536# 比较两个列表元素cmp(L1, L2)# 生成列表L = range(1,4) # [1, 2, 3]### 尾部添加元素L.append(4) # [1, 2, 3, 4]### 指定位置添加元素L.insert(2,66) # [1, 2, 66, 3, 4] 第一个值是索引位置,第二个值是新元素### 删除最后一个元素L.pop() # [1, 2, 66, 3]### 删除指定位置元素L.pop(2) # [1, 2, 3] 传入的 2 是索引位置### 计算表达式的长度print(len(L)) # 3 ### list合并拼接zip([1,2,3],['A','B','C']) # [(1,'A'),(2,'B'),(3,'C')][1,2,3] + ['A','B','C'] # [1,2,3,'A','B','C'][1,2,3]*2 # [1, 2, 3, 1, 2, 3]### 迭代中拿到索引(迭代取出的都是元素本身的值)for index,value in enumerate(L): print(index, ':' , value) # 0 : 1 1 : 2 2 : 3 使用enumerate(L)for index,value in zip(range(0,3),L): print(index, ':' , value) # 0 : 1 1 : 2 2 : 3 使用zip()拼接的方法# 列表推导式L = [i**2 for i in range(1, 10)] # [1, 4, 9, 16, 25, 36, 49, 64, 81]L = [i**2 for i in range(1, 10) if i <= 5] # [1, 4, 9, 16, 25] 常用列表方法:| 名称 | 用途 ||———–|———–|———–|| L.index(value, begin, end) | 返回对应元素的索引下标,不存在时报错 || L.insert(index, value) | 在index的索引处插入value || L.count(i) | 返回i的个数 || L.remove(i) | 删除第一次出现在列表中的i || L.copy() | 复制L,只复制一层(浅拷贝),相当于L[:] || L.append(i) | L列表最后添加单个元素 || L.extend(list) | 相当于合并两个列表 || L.clear() | 清空列表 || L.reverse() | 反转列表 || L.sort() | 将列表中的元素进行排序,默认从小到大 || L.pop(index) | 默认最后一个,指定索引则删除对应元素 | 深拷贝和浅拷贝:123456L1 = L # 不拷贝,创建一个变量同事绑定原对象L2 = L.copy() # 浅拷贝,等同于 L2 = L[:]import copyL3 = copy.deepcopy(L) # 深拷贝 元组 tuple创建:构造函数:tuple() ,可用 () 创建,空元组直接用 () 创建,单个元素的元素后加,,避免歧义,如 (1,)取值:根据索引,如 T[0] 取出索引为 0 的值;切片,如:T[0:3] 取出从索引 0-3 的值,其中不包括索引 3 的值特点:有序集合。一旦创建不能修改(不支持索引赋值和切片赋值),每个元素的指向永远不变,但是如果指向的是一个list,list是可变的序列赋值:x, y, z = (100, 200, 300)元组方法:T.index(value, begin, end)和T.count(i)12345678910# 单个元组元素后加逗号,避免歧义t = 1, # 相当于:元组 (1,)t = 1, 2, 3 # 相当于:元组 (1, 2, 3)t = (1) # 相当于:整数 1 括号的作用:提高优先级# 比较两个元组元素cmp(T1, T2)# 列表转换为元组tuple(L) 字典 dict创建:构造函数:dict(),如:dict(a=1, b=2),可用 {} 创建,如: {key: value} 的形式,和JS中的 map、object 类似取值:根据 key,如 D[key] 取出 key 的 value;get方法,如:D.get[key]特点:无序集合(打印顺序和创建顺序可能不一样)。查找速度快。内存占用大。key元素必须是不可变的,所以不能是 list 。key 不重复字典推导式:{ 键:值 for 变量 in 可迭代对象 if 真值表达式 in 可迭代对象 }12345678910# 以列表返回一个字典所有的键D.keys()# 迭代字典中的 value D.values() # 把 value 转为 listD.itervalues() # 不转为 list,省内存# 迭代字典中的 key 和 value D.items() # 把 value 转为 listD.iteritems() # 不转为 list,省内存 集合 set创建:构造函数:set(可迭代) ,可用 {} 创建,如:{value, value},但是空集合只能用set()创建,{}表示空字典取值:不能通过索引来访问值,但是可用 in 进行判断特点:无序集合。不包含重复元素。判断元素是否在set中很快。储存的元素是不可变对象(可以用in判断但不能循环,可以新增和添加但不能修改),集合中只能放不可变类型,可变类型不能放进去,如:列表、字典、集合与字典的区别:字典有键和值,集合没有值,值即键,如:{ 1, 2, 3 }为集合,有键值对的为字典,{}为空字典集合推导式:{ 表达式 for 变量 in 可迭代对象 if 真值表达式 },if子句可省略集合的运算:1234567891011121314151617# 交集 &{ 1, 2, 3 } & { 3, 4, 5 } # { 3 } # 并集 |{ 1, 2, 3 } | { 3, 4, 5 } # { 1, 2, 3, 4, 5 } # 补集 - { 1, 2, 3 } - { 3, 4, 5 } # { 1, 2 } s1 - s2# 对称补集 ^{ 1, 2, 3 } ^ { 3, 4, 5 } # { 1, 2, 4, 5 } (s1 - s2) | (s2 - s1)# 子集 <{ 3 } < { 3, 4, 5 } # True s1 < s2 # 超集 >{ 1, 2, 3 } > { 3 } # True s1 > s2 集合的方法:| 名称 | 用途 ||———–|———–|———–|| S.add(i) | 集合中添加新元素,如果已存在不会添加进去,但是也不会报错 || S.remove(i) | 集合中删除元素,元素不存在报错 || S.discard(i) | 集合中移除元素,元素不存在什么也不做 || S.copy() | 浅拷贝集合 || S.clear() | 清空集合 || S.pop() | 集合中删除一个随机元素(默认第一个元素?) || S.update(s2) | 相当于合并两个集合 | 固定集合 forzenset构造函数:forzenset(可迭代序列)特点:不可变的、无序的、含有唯一元素的集合作用:可以作为字典的键,还可以做集合的值]]></content>
<categories>
<category>后端</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[QUIC协议 和 TCP/UDP 协议]]></title>
<url>%2F2019%2F04%2F11%2FQUIC%E5%8D%8F%E8%AE%AE-%E5%92%8C-TCP-UDP-%E5%8D%8F%E8%AE%AE%2F</url>
<content type="text"><![CDATA[目的:熟悉了解 QUIC协议 和 TCP/UDP 协议。 HTTP 协议历史 HTTP 0.9(1991年)只支持get方法不支持请求头 HTTP 1.0(1996年)基本成型,支持请求头、富文本、状态码、缓存、连接无法复用 HTTP 1.1(1999年)支持连接复用、分块发送、断点续传 HTTP 2.0(2015年)二进制分帧传输、多路复用、头部压缩、服务器推送等 HTTP 3.0(2018年)QUIC 于2013年实现、2018年正式更名为HTTP3 HTTP1.0 和 HTTP1.1 队头阻塞:下个请求必须在前一个请求返回后才能发出,导致带宽无法被充分利用,后续请求被阻塞(HTTP 1.1 尝试使用流水线(Pipelining)技术,但先天 FIFO(先进先出)机制导致当前请求的执行依赖于上一个请求执行的完成,容易引起队头阻塞,并没有从根本上解决问题) 协议开销大:header里携带的内容过大,且不能压缩,增加了传输的成本 传输不安全:采用文本形式传输,所有传输的内容都是明文,且客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性 单向请求:只能单向请求,客户端请求什么,服务器返回什么 HTTP 1.0 和 HTTP 1.1 的区别: HTTP 1.0:仅支持保持短暂的TCP连接(连接无法复用);不支持断点续传;前一个请求响应到达之后下一个请求才能发送,存在队头阻塞 HTTP 1.1:默认支持长连接(请求可复用TCP连接);支持断点续传(通过在 Header 设置参数);优化了缓存控制策略;管道化,可以一次发送多个请求,但是响应仍是顺序返回,仍然无法解决队头阻塞的问题;新增错误状态码通知;请求消息和响应消息都支持Host头域 HTTP2解决 HTTP1 的一些问题,但是解决不了底层 TCP 协议层面上的队头阻塞问题。2015年 二进制传输:二进制格式传输数据解析起来比文本更高效 多路复用:重新定义底层 http 语义映射,允许同一个连接上使用请求和响应双向数据流。同一域名只需占用一个 TCP 连接,通过数据流(Stream)以帧为基本协议单位,避免了因频繁创建连接产生的延迟,减少了内存消耗,提升了使用性能,并行请求,且慢的请求或先发送的请求不会阻塞其他请求的返回 Header压缩:减少请求中的冗余数据,降低开销 服务端可以主动推送:提前给客户端推送必要的资源,这样就可以相对减少一点延迟时间 流优先级:数据传输优先级可控,使网站可以实现更灵活和强大的页面控制 可重置:能在不中断 TCP 连接的情况下停止数据的发送 缺点:HTTP 2中,多个请求在一个TCP管道中的,出现了丢包时,HTTP 2的表现反倒不如HTTP 1.1了。因为 TCP 为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP 2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求。而对于 HTTP 1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据 HTTP3HTTP 是建立在 TCP 协议之上,所有 HTTP 协议的瓶颈及其优化技巧都是基于 TCP 协议本身的特性,HTTP2 虽然实现了多路复用,底层 TCP 协议层面上的问题并没有解决(HTTP 2.0 同一域名下只需要使用一个 TCP 连接。但是如果这个连接出现了丢包,会导致整个 TCP 都要开始等待重传,后面的所有数据都被阻塞了),而 HTTP3 的 QUIC 就是为解决 HTTP2 的 TCP 问题而生。 TCP 是什么?TCP:全称为传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。TCP 是面向连接的、可靠的流协议。 面向连接(存在三次握手) 只能进行点对点的数据传输 面向字节流(TCP 不像 UDP 一样那样一个个报文独立地传输,而是在不保留报文边界的情况下以字节流方式进行传输) 可靠传输(依靠 TCP 的段编号以及确认序号) 拥塞控制(网络出现拥塞的时候,TCP 能够减小向网络注入数据的速率和数量,缓解拥塞) 提供全双工通信(通信双方的应用程序在任何时候都能发送数据) 存在队头阻塞(使用序列号来标识数据的顺序,数据必须按照顺序处理) UDP 是什么?UDP:全称为用户数据报协议,与 TCP 协议一样用于处理数据包,是一种面向无连接不可靠的协议。关注把数据报文传输出去,不关心是否安全到达。 面向无连接(不需要三次握手) 支持一对多,多对多,多对一 不可靠(原因:通信都不需要建立连接、没有拥塞控制所以网络条件不好的情况下可能会导致丢包) 传输数据高效 QUIC 对比 TCP/UDP 的优缺点QUIC 协议是 Google 提出的一套基于 UDP 的开源协议,它汇集了 TCP和 UDP 的优点,传输高效并且可靠。QUIC 的优点: 迭代更新快 TCP 和 UDP 协议是操作系统内核实现的,部署进度慢。QUIC直接基于客户端实现,能实现快速迭代更新 没有队头阻塞的多路复用 TCP 使用序列号来标识数据的顺序,数据必须按照顺序处理,可能会造成队头阻塞。HTTP2 支持多路复用,但是由于强制使用 TLS,还存在一个 TLS 协议层面的队头阻塞,QUIC 最基本的传输单元是 Packet,不会超过 MTU 的大小,整个加密和认证过程都是基于 Packet 的,不会跨越多个 Packet。这样就能避免 TLS 协议存在的队头阻塞。Stream 之间相互独立,比如 Stream2 丢了一个 Pakcet,不会影响 Stream3 和 Stream4,所以也不存在 TCP 队头阻塞。 握手更迅速 TCP需三次握手才能建立连接,有等待时延,如果用了TLS加密,还会进一步增加时延。QUIC采用了类似于TCP Fast Open的设计,在之前已经连接过的情况下可以无需握手,直接开始传送数据,连接建立时延为0 加密技术比TLS性能好,同时具有各种攻击防御策略。 前向纠错 QUIC和TCP一个主要的核心区别就是:TCP采用 重传 机制,而QUIC采用 纠错 机制。如果发生丢包的话,TCP首先需要一个等待延时来判断发生了丢包,然后再启动重传机制,在此期间会对连接造成一定的阻塞(并且TCP窗口是缓慢增大的,Web这种突发性快速连接情况下窗口会相对较小),从而影响传输时间。而QUIC采用了一种脑洞极大的前向纠错(FEC)方案,类似于RAID5,将N个包的校验和(异或)建立一个单独的数据包发送,这样如果在这N个包中丢了一个包可以直接恢复出来,完全不需要重传,有利于保证高速性,N可以根据网络状况动态调整 连接保持 TCP 连接是由四元组标识的(源 IP,源端口,目的 IP,目的端口),当其中一项发生改变,都需要重新建立和服务端的 TCP 连接。QUIC 连接不再以 IP 及端口四元组标识,而是以一个 64 位的随机数作为 ID 来标识,这样就算 IP 或者端口发生变化时,只要 ID 不变,这条连接依然维持着。这就意味着:在IP地址和端口变化的情况下(比如从Wi-Fi切换到流量),可以无需重新建立连接,继续通信 能实现证书压缩,减少证书传输量,针对包头进行验证等。 QUIC 的不足: QUIC 基于 UDP ,目前很多网络运营商会降低 UDP 包的优先级,使得 UDP 丢包率特别高 支持的浏览器少 参考资料:下一代通信协议:QUIC科普:QUIC协议原理分析HTTP/3 都来了,你却还在用 HTTP/1.1?TCP和UDP比较一文读懂 HTTP/2 及 HTTP/3 特性为什么 HTTP1.1 不能实现多路复用解读HTTP/2与HTTP/3 的新特性]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTTP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[利用本地项目构建Chrome插件]]></title>
<url>%2F2019%2F03%2F28%2F%E5%88%A9%E7%94%A8%E6%9C%AC%E5%9C%B0%E9%A1%B9%E7%9B%AE%E6%9E%84%E5%BB%BAChrome%E6%8F%92%E4%BB%B6%2F</url>
<content type="text"><![CDATA[最近在修改一个todoList小Demo,突然想到如果把它设置成 Chrome 插件并设置为 newtab 的默认页面,那么每次打开新页面就可以看到自己的 todo 啦,还可以提醒自己按时完成任务,于是就开始了尝试,并随手总结如下: 1 配置 manifest.json把普通项目变成Chrome 插件的核心是配置一个名为 manifest.json的文件并添加到根目录中点击查看配置文档简单的manifest.json配置项如下:1234567891011121314151617181920{ // 插件名,必写 "name": "Todo", // 版本号,必写 "version": "1.0", // 清单文件的版本,必写,值为2 "manifest_version": 2, // 描述 "description": "My todo", // 插件的图标,和 manifest.json 文件同一个目录下 "browser_action": { "default_icon": "icon.png" }, // newtab的默认页面, manifest.json 文件同一个目录下 "chrome_url_overrides": { "newtab": "index.html" }, // 默认语言 "default_locale": "zh_CN",} 2 浏览器添加插件1 打开 Chrome – 设置 – 扩展程序 ,或者直接在地址栏输入:chrome://extensions2 在扩展程序页面右上角勾选 开发者模式– 点击左上角的 加载正在开发的扩展程序按钮 – 选择扩展所在的文件夹,此时在浏览器工具栏中看到新添加的扩展。3 在浏览器窗口新开一个标签页,此时新标签页初始显示的就是刚刚设置的导航页啦~~~ 3 解决 vue.js 写的插件在浏览器打开一片空白的问题出现空白的原因Vue官方解释如下::“有些环境,如 Google Chrome Apps,会强制应用内容安全策略Content Security Policy (CSP) ,不能使用 new Function() 对表达式求值。这时可以用 CSP 兼容版本。完整版本依赖于该功能来编译模板,所以无法在这些环境下使用。””另一方面,运行时版本则是完全兼容 CSP 的。当通过 webpack + vue-loader 或者 Browserify + vueify 构建时,模板将被预编译为 render 函数,可以在 CSP 环境中完美运行” 解决方法:1 下载 csp 兼容版本的vue(最新的只有vue 1.x的)2 使用安全策略方案,在manifest.json文件添加如下一行:123{ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';"} 实践项目地址:普通的 vue的 ps: vue项目的manifest.json文件是放在打包文件dist目录里,设置插件的时候选择dist目录即可。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
<tag>Browser</tag>
</tags>
</entry>
<entry>
<title><![CDATA[理解 Object.defineProperty 和 Proxy]]></title>
<url>%2F2019%2F03%2F14%2F%E7%90%86%E8%A7%A3-Object-defineProperty-%E5%92%8C-Proxy%2F</url>
<content type="text"><![CDATA[理解 Object.defineProperty 和 Proxy 1 Object.defineProperty(obj, prop, descriptor)obj:目标对象prop:需定义或修改的属性的名字descriptor:目标属性所拥有的特性 数据描述符有以下键值1234567891011let obj = {}Object.defineProperty(obj,"key",{ value: '', writable: true | false, configurable: true | false, enumerable: true | false,})// value: 设置属性的值// writable: 值是否可以重写。true | false// enumerable: 目标属性是否可以被枚举。true | false// configurable: 目标属性是否可以被删除或是否可以再次修改特性。 true | false ps:一旦使用 Object.defineProperty 给对象添加属性,那么如果不设置属性的特性,那么 configurable、enumerable、writable 这些值都为默认的 false 。如果不使用 Object.defineProperty 添加属性,那么 configurable、enumerable、writable 这些值都为默认的 true 存取描述符有以下键值123456789let obj = {}Object.defineProperty(obj,"key",{ get:function (){} | undefined, set:function (value){} | undefined, configurable: true | false, enumerable: true | false,})// get: 给属性提供 getter 的方法,如果无 getter 则为 undefined// set: 给属性提供 setter 的方法,如果无 setter 则为 undefined,设置的新值可通过value获取 ps:当使用了 getter 或 setter方法,不允许使用 writable 和 value 这两个属性。使用示例:123456789101112131415161718let obj = {}let value = 'old value'Object.defineProperty(obj,"key",{ get:function (){ //获取值时触发 return value }, set:function (newvalue){ //设置新值时触发,设置的新值可通过 newvalue 获取 value = newvalue }})//获取值console.log( obj.key ) // old value//设置值obj.key = 'new value'console.log( obj.key ) // new value 2 new Proxy(target, handler)Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等),通过操作为对象生成的代理器,实现对对象各类操作的拦截式编程target:要代理的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)handler:一个对象,其属性是当执行一个操作时定义代理的行为的函数。相当于拦截器,可以有多个拦截操作 使用示例:123456789101112131415161718192021222324252627let handler = {// set 有3个参数,obj(对象), prop(属性), value(新添加的值),get 有2个参数,obj, propset: function(obj, prop, value) { // 如参数名是 age 就执行如下的判断(报错则无法执行赋值操作,赋值无效) if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('The age is not an integer') } if (value > 200) { throw new TypeError('The age seems invalid') } } // 赋值 obj[prop] = value; }}let person = new Proxy({}, handler)person.age = 100console.log(person.age) // 100person.age = 'young' // 抛出异常: Uncaught TypeError: The age is not an integerperson.age = 300 // 抛出异常: Uncaught TypeError: The age seems invalidperson.name = 'Nola' // "Nola"console.log(person) // Proxy {age: 100, name: "Nola"} 3 Object.defineProperty VS ProxyObject.defineProperty缺点: Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。 在Vue中,Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 Proxy优点: 可以劫持整个对象,并返回一个新对象。 Proxy可以直接监听数组的变化 有多种劫持操作。 Proxy缺点: Proxy是es6提供的新特性,兼容性不好,且这个属性无法用polyfill来兼容。 参考资料:ECMAScript 6 入门-ProxydefineProperty VS Proxy实现双向绑定 Proxy比 defineproperty优劣如何]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[vue组件的数据通信方式]]></title>
<url>%2F2019%2F02%2F02%2Fvue%E7%BB%84%E4%BB%B6%E7%9A%84%E6%95%B0%E6%8D%AE%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F%2F</url>
<content type="text"><![CDATA[三种通信方式整理 1 组件的数据通信方式—引用类型数据index.js中传一个引用类型的数据obj:{age: ..}12345678910111213141516new Vue({ data: { //传一个引用类型的数据 obj: { age: 20 } }} ``` index.html 中加:obj="obj" ```html<body> <div id="app"> <foot :obj="obj"></foot> </div></body> Foot.vue 中加props: [‘obj’]接收子组件件内部不能对父组件传的数据进行修改,所以,data中需加ob:obj,再在生命周期created()中设一个定时器修改值12345678910111213export default { props: ['obj'], data() { return { ob: this.obj } }, created(){ setTimeout(() => { this.ob.age = 18 },200) }} 运行结果:内部:ob.age=18外部:obj.age=18 (obj的值被改变了 20-18) 2 组件的数据通信方式—自定义事件如果把Foot.vue中的内部ob的值改成深拷贝的形式,则不会影响到外部obj的值Foot.vue中123456data() { return { // ob: this.obj 改成: ob: window.JSON.parse(window.JSON.stringify(this.obj)) }}, 运行结果:内部ob.age=18 (深拷贝后,内部改变,外部不受影响)外部obj.age=20 JSON 通常用于与服务端交换数据,在接收服务器数据时一般是字符串,可使用 JSON.parse() 方法将数据转换为 JavaScript 对象。如果像 ob 改成深拷贝形式,同时也想让外面 obj 的值也改变,则需要用到自定义事件Foot.vue中123456created(){ setTimeout(() => { this.ob.age = 18 this.$emit('change',18) // 把值传出去 },4000)} 在index.html中绑定自定义事件1<foot :obj="obj" @change=“changeAge”></foot> 同时在index.js中定义方法接收1234changeAge(age){ this.obj.age = age console.log(age)} 运行结果:内部ob.age=18外部obj.age=18 (自定义事件使外部值改变) 3 组件的数据通信方式—全局事件var Event = new Vue();相当于又 new 了一个 vue 实例,Event 中含有 vue 的全部方法;Event.$emit(‘msg’,this.msg);发送数据,第一个参数是发送数据的名称,接收时还用这个名字接收,第二个参数是这个数据现在的位置;Event.$on(‘msg’,function(msg));接收数据,第一个参数是数据的名字,与发送时的名字对应,第二个参数是一个方法,要对数据的操作;建立一个全局的foo.js,内容如下:123import Vue from 'vue'const foo = new Vue()export default foo 在index.js中引入foo并在生命周期中添加监听12345678import foo from 'js/foo.js'//created中created(){ foo.$on('change',(age) => { this.obj.age = age })} Foot.vue中 created(){ setTimeout(() => { this.ob.age = 18 //this.$emit('change',18) foo.$emit('change',18) // 改成全局事件 },4000) } 不需要再用 changeAge 的方式监听]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Vue</tag>
</tags>
</entry>
<entry>
<title><![CDATA[多种方法给数组去重]]></title>
<url>%2F2018%2F12%2F03%2F%E6%95%B0%E7%BB%84%E5%8E%BB%E9%87%8D%2F</url>
<content type="text"><![CDATA[数组去重的几种方法数组 [1,1,2,3,2,3] 去重变成 [1, 2, 3] 的多种方法 Array.from() + new Set() (es6新增)Array.from()文档Set()文档123function unique(arr) { Array.from(new Set(arr))} 扩展运算符 + new Set()1arr = [...new Set(arr)] Array.prototype.includes() (es6新增)filter()文档123456789function unique(arr) { var newArr = [] arr.forEach(item => { if(!newArr.includes(item)) { newArr.push(item) } }) return newArr} Array.prototype.filter() (es5新增)filter()文档12345function unique(arr) { return arr.sort().filter((item,index,arr) => { return item !== arr[index+1] })} Array.prototype.splice()splice()文档12345678function unique(arr) { arr.sort().forEach((item,index) => { if(arr[index] === arr[index++]) { arr.splice(index,1) } }) return arr} 一个实践例子123456789101112131415161718192021222324252627// exampleunique([1,'1',1]) -> [1,'1']unique([{a: 1}, {b: 1}, {a: 1}]) -> [{a: 1}, {b: 1}]unique([{a: 1, b: 2}, {b: 1}, {b: 2, a: 1}]) -> [{a: 1, b: 2}, {b: 1}]unique([[1, {a: 1}], [2], [3], [1, {a: 1}]]) -> [[1, {a: 1}], [2], [3]]// 参考答案function unique(arr) { var hash = {}; for (var i = 0, len = arr.length; i < len; i++) { var item = arr[i]; var key = typeof item + JSON.stringify(item); if (!hash[key]) { hash[key] = true } else { arr.splice(i, 1) len-- i-- } } return arr;}// ES6 写法function unique(arr) { return Array.from(new Set(arr.map(JSON.stringify))).map(JSON.parse)}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[js获取url参数值]]></title>
<url>%2F2018%2F12%2F01%2Fjs%E8%8E%B7%E5%8F%96url%E5%8F%82%E6%95%B0%E5%80%BC%2F</url>
<content type="text"><![CDATA[js获取url参数值的各种方法 用正则的方法匹配123456789101112131415161718192021function GetValue() { // 把地址?后面的值匹配出来 如:?token=1&from=2 var url = location.href.match(/\?.*/)[0] var map = new Object(); if (url.indexOf("?") != -1) { // 删掉问号 如:token=1&from=2 var str = url.substr(1); // 多个参数变成一个数组 如:{"token=1","from=2"] var strs = str.split("&"); for(var i = 0; i < strs.length; i ++) { // 把参数的值提取出来 如:["token","1"],["from","2"] var temp = strs[i].split("=") // 第一个值为key,第二个值为value 如:{"token": "1"} map[temp[0]]=unescape(temp[1]) } } return map; } var value = GetValue()// 查找参数from的值console.log(value.from)]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[LeetCode答题记录 easy]]></title>
<url>%2F2018%2F10%2F30%2FLeetCode%E7%AD%94%E9%A2%98%E8%AE%B0%E5%BD%95%2F</url>
<content type="text"><![CDATA[LeetCode上的答题记录,方便日后来围观这个愚蠢的我哈哈 53 Maximum Subarray给定整数数组nums,找到具有最大总和并返回其总和的连续子数组(包含至少一个数字)。题目链接Exp 1: Input: [-2,1,-3,4,-1,2,1,-5,4] Output: 6Explanation: [4,-1,2,1] has the largest sum = 61234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// ● success Runtime: xx ms``` ### 136 Single Number给一个非空的整数数组,除了一个元素外,每个元素都会出现两次。找到那一个。要求时间复杂度是O(n),空间复杂度是O(1)。[题目链接](https://leetcode.com/problems/single-number/description/)Exp 1: `Input: [2,2,1] Output: 1`Exp 2: `Input: [4,1,2,1,2] Output: 4````JavaScript// 第一次尝试:用indexOf查找的方法// ● success Runtime: 188 ms var singleNumber = function(nums) { var result = [] nums.forEach((value,index) => { var idx = result.indexOf(value) if(idx < 0) { result.push(value) } else { result.splice(idx,1) } }) return Number(result)}// 第二次尝试:用sort()排序后判断的方法// ● success Runtime: 80 ms var singleNumber = function(nums) { nums.sort((a,b) => a-b) for(let i = 0; i < nums.length; i++) { if(nums[i] !== nums[i-1] && nums[i] !== nums[i+1]) return nums[i] }}// 第三次尝试:利用eval()对字符串中的内容进行位运算// ● success Runtime: 68 ms var singleNumber = function(nums) { return eval(nums.join('^'))}// 第四次尝试:使用位运算,'^'是一种运算符,运算符!!!// ● success Runtime: 56 ms var singleNumber = function(nums) { var result for(let i = 0; i < nums.length; i++) result = nums[i] ^ result return result}// ● success Runtime: 56 ms var singleNumber = function(nums) { return nums.reduce((a,b) => a ^ b)} 929 Unique Email Addresses在电子邮件地址的本地名称部分中的某些字符之间添加句点(’。’),点会被忽略掉。在本地名称中添加加号(’+’),会忽略第一个加号后面的所有内容。给定电子邮件列表,有多少不同的地址实际接收邮件?题目链接Exp 1:Input: ["[email protected]","[email protected]","[email protected]"]Output: 2123456789101112131415161718// 1 先用正则把@后面的值匹配出来,2 再把匹配出的@前面的值变成数组,3 找到第一个"+"的index位置,// 4 截取从0到index范围的数组,5 变成字符串并用正则去掉".",6 最后把前后拼起来放到一个数组,最后去重// ● success Runtime: 108 msvar numUniqueEmails = function(emails) { var result = [] emails.forEach((value,index)=>{ let end = value.match(/@.*/)[0] let tempArr = value.match(/.*\@/)[0].split('') let start = tempArr.slice(0,tempArr.indexOf("+")).join('').match(/\w/g).join('') result.push(start.concat(end)) }) return Array.from(new Set(result)).length}// ● success Runtime: 68 msvar numUniqueEmails = function(emails) { return Array.from(new Set(emails.map(email => email.split('@')[1]))).length} 617 Merge Two Binary trees给定两个二叉树,将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。注意: 合并必须从两个树的根节点开始。题目链接1234567891011121314151617// ● success Runtime: 92 msvar mergeTrees = function(t1, t2) { // 加外面这层的目的是为了处理t1,t2都是空的情况 if(!t1 && !t2) return [] if(!t1) return t2 if(!t2) return t1 function merge(t1, t2) { if(!t1) return t2 if(!t2) return t1 t1.left = merge(t1.left, t2.left) t1.right = merge(t1.right, t2.right) t1.val = t1.val + t2.val return t1 } merge(t1, t2) return t1} 198 House Robber你是一个专业的强盗,计划在街上抢劫房屋。每个房子都藏着一定数量的钱,阻止你抢劫他们的唯一限制因素是相邻的房屋有连接的安全系统,如果两个相邻的房子在同一个晚上被闯入,它将自动联系警方。给出一个代表每个房子的金额的非负整数列表,确定今晚可以抢劫的最大金额而不警告警察。题目链接Exp 1:Input: [1,2,3,1] Output: 4Exp 2:Input: [2,7,9,3,1] Output: 12123456789101112// ● success Runtime: 48 msvar rob = function(nums) { var even = 0, odd = 0 for (let i = 0; i < nums.length; i++) { if (i % 2 === 0) { even = Math.max(even + nums[i], odd) } else { odd = Math.max(even, odd + nums[i]) } } return Math.max(even, odd)} 20 Valid Parentheses给定一个只包含字符’(’,’)’,’{‘,’}’,’[‘和’]’的字符串,如果对应的括号能正常关闭,则字符串有效。题目链接Exp 1: Input: "()" Output: trueExp 2: Input: "()[]{}" Output: trueExp 3: Input: "(]" Output: falseExp 4: Input: "([)]" Output: falseExp 5: Input: "{[]}" Output: true12345678910111213141516171819202122232425262728// ● success Runtime: 56 msvar isValid = function(s) { s = Array.from(s) var stack= [],contrast = [] var map = { "(" : ")", "{" : "}", "[" : "]" } s.forEach( (value, index) => { // 是左括号就压栈,是右括号就出栈,出栈时判断左右括号是否匹配 if(map[value]) { stack.push(value) } else { var temp = stack.pop() if(map[temp] !== value) { contrast.push("false") } } }) // 如果栈中长度或者不匹配元素的长度不等于0,则返回false if(contrast.length > 0 || stack.length > 0) { return false } else { return true }}// 不知道为什么,把contrast.push("false")换成return false总是会出问题,只能增加一个contrast数组用来存那些不匹配的,最后再进行判断 203 Remove Linked List Elements给一个val,删掉链表中值和val一样的所有元素。题目链接Exp 1: Input: 1->2->6->3->4->5->6, val = 6 Output: 1->2->3->4->5123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// 第一次尝试链表变成数组进行操作,返回一个数组,虽然测试成功,但并不是在链表中移除元素// ● success Runtime: 96 msvar removeElements = function(head, val) { // 链表变成数组 var headArr = [] while(head !== null) { headArr.push(head.val) head = head.next } // 删除数组中的匹配元素 var tempArr = []; var idx = headArr.indexOf(val); while (idx != -1) { tempArr.push(idx); idx = headArr.indexOf(val, idx + 1); } tempArr.sort((a,b) => b-a) tempArr.forEach( (value, index) => { headArr.splice(value,1) }) return headArr}// 第二次尝试直接在链表中操作// ● success Runtime: 76 msvar removeElements = function(head, val) { var node = val var curNode = head // 如果head不存在,返回[] if(!head) return [] // 查找和删除 while(curNode.next != null) { while (curNode.next != null && curNode.next.val !== node) { curNode = curNode.next } if(curNode.next !== null ) { curNode.next = curNode.next.next } } // 判断head.val,即第一个元素,如果等于node就删除 if(head.val === node) head = head.next return head} 504 Base 7实现一个七进制。题目链接Exp 1: Input: 100 Output: "202"Exp 2: Input: -7 Output: "-10"12345678910111213141516171819202122// ● success Runtime: 64 msvar convertToBase7 = function(num) { // num为0,直接返回0 if(num === 0) return "0" // num大于0,取num的绝对值 nums = num > 0? num:-num // 算出来的结果存到数组 var result = [] while(nums > 0){ res = nums % 7 nums = parseInt(nums/7) result.unshift(String(res)) } // 判断num的符号,是负数就加上负号 if(Math.sign(num) === -1) result.unshift('-') // return字符串形式 return result.join('')} 859 Buddy Strings改变A中随意两个元素的位置,使A和B相等,能相等true,不能false。题目链接Exp 1: Input: A = "ab", B = "ba" Output: trueExp 2: Input: A = "ab", B = "ab" Output: falseExp 3: Input: A = "aa", B = "aa" Output: trueExp 4: Input: A = "aaaaaaabc", B = "aaaaaaacb" Output: trueExp 5: Input: A = "", B = "aa" Output: false1234567891011121314151617181920212223242526272829303132333435363738394041424344// ● success Runtime: 60 msvar buddyStrings = function(A, B) { // 两个字符串长度不一致,返回false if(A.length !== B.length) return false // 数组长度小于两个,返回false if(A.length < 2) return false // 变成数组 A.split() B.split() // 两个数组相等时 if(String(A) === String(B)) { // 数组长度只有两个,A中的两个值相等,返回true if(A[0] === A[1] && A.length === 2) { return true } // A中有重复的值得话,返回true if(Array.from(new Set(A)).length < A.length) { return true } return false } // 设两个数组分别存A中和B中不一样的部分 var tempA = [],tempB = [] for(let i = 0; i < A.length; i++) { if(A[i] !== B[i]) { tempA.push(A[i]) tempB.push(B[i]) } } // 不同的数字超过两个就返回false if(tempA.length > 2) return false // 如果A、B不一样的部分排序后还是一样,返回true if(tempA.sort().join('') === tempB.sort().join('')) { return true }else{ return false }}// 传说中的送分题,我挂了6次才success,还有谁? 438 (未完成)Find All Anagrams in a String给定一个字符串s和一个非空字符串p,找到s中p的的所有起始索引,只要和p的元素一样就可以,不管顺序。题目链接Exp 1: Input: s: "cbaebabacd" p: "abc" Output: [0, 6]Exp 2: Input: s: "abab" p: "ab" Output: [0, 1, 2]1234567891011121314151617181920212223242526272829303132333435363738// 第一次尝试,在数组中循环遍历查找// ○ failed Time Limit Exceededvar findAnagrams = function(s, p) { // 把字符串变成数组 s = s.split('') p = p.split('') // 把p数组排序并变成字符串,方便后面比较 var strP = p.sort().join('') // 定义result存最终的结果 var result = [] p.forEach(function(value, index) { // 遍历p数组 s.forEach(function(val, ind) { // 遍历s,如果p中的值在s中匹配成功 if(value === val) { // 设一个临时数组temp存匹配的数字的后面几个(长度和p的长度一样) let temp = [] for (let i = 0; i < p.length; i++) { temp.push(s[ind + i]) } // 把临时数组temp排序并变成字符串 let strTemp = temp.sort().join('') // 比较p数组和临时数组temp中的内容是否一样,一样就把对应的s数组中的指针存到result中 if(strTemp === strP) { result.push(ind) } } }) }) // 把result数组去重并重新排序,sort默认是按unicode码顺序排列,数字排序有问题,需添加方法 result = Array.from(new Set(result)).sort(function(a,b) {return a - b}) return result}// 第二次尝试,滑动窗口(sliding window)解决字符串查找匹配的问题 7 Reverse Integer给一个32位有符号整数,返回这个整数的反向数字。题目链接Exp 1: Input: 123 Output: 321Exp 2: Input: -123 Output: -321Exp 3: Input: 120 Output: 211234567// ● success Runtime: 88 msvar reverse = function(x) { let num = parseInt(x.toString().split('').reverse().join('')) if (num > Math.pow(2,31) - 1) return 0; return Math.sign(x) * num}// Math.sign() 函数返回一个数字的符号, 指示数字是正数,负数还是零 707 Design Linked List设计一个链表,单链表具有两个属性:val和next。 val是当前节点的值,next是指向下一个节点的指针/引用。如果要使用双向链表,需要一个属性prev以指示链接列表中的上一个节点。假设链表中的所有节点都是0索引的。题目链接在链表类中实现这些功能:get(index):获取链表中索引节点的值。如果索引无效,则返回-1。addAtHead(val):在链表的第一个元素之前添加值为val的节点。插入后,新节点将成为链表的第一个节点。addAtTail(val):将值val的节点追加到链表的最后一个元素。addAtIndex(index,val):在链表中的索引节点之前添加值为val的节点。如果index等于链表的长度,则该节点将附加到链表的末尾。如果index大于长度,则不会插入节点。deleteAtIndex(index):如果索引有效,则删除链表中的索引节点。123456789101112131415161718192021222324252627282930313233343536// ● success Runtime: 72 msvar MyLinkedList = function() { this.arr = []}MyLinkedList.prototype.get = function(index) { if(index < this.arr.length && index > -1){ return this.arr[index] }else{ return -1 }}MyLinkedList.prototype.addAtHead = function(val) { this.arr.unshift(val) return this.arr}MyLinkedList.prototype.addAtTail = function(val) { this.arr.push(val) return this.arr}MyLinkedList.prototype.addAtIndex = function(index, val) { if(index <= this.arr.length && index > -1) { this.arr.splice(index, 0, val) return this.arr }else{ return }}MyLinkedList.prototype.deleteAtIndex = function(index) { if(index < this.arr.length && index > -1){ this.arr.splice(index, 1) return this.arr }else{ return }}// 虽然测试成功了,但、好像、似乎...并不是可以用于链表的方法 665 Non-decreasing Array给定一个包含n个整数的数组,通过修改最多1个元素来检查它是否可以变为非递减。如果array [i] <= array [i + 1]为每个i1 <= i <n保持,则这个数组是非递减的。题目链接Exp 1: Input: [4,2,3] Output: TrueExp 2: Input: [4,2,1] Output: False12345678910111213141516171819202122232425262728293031323334// ● success Runtime: 132 msvar checkPossibility = function(nums) { // 前一个<=后一个,存一个"true",反之存一个"false"到result数组中 var result = [] nums.forEach(function(value,index) { if(nums[index] <= nums[index + 1]) { result.push("true") }else{ result.push("false") } }) // 删除result数组的最后一个 result.pop() // falseArr数组存result数组中"false"值的index var falseArr = []; var idx = result.indexOf("false"); while (idx != -1) { falseArr.push(idx); idx = result.indexOf("false", idx + 1); } // 判断 if(falseArr.length === 1) { let ind = falseArr[0] if(nums[ind] <= nums[ind + 2] || nums[ind - 1] <= nums[ind + 1] || ind === 0 || ind === result.length - 1) { return true }else{ return false } }else if(falseArr.length < 1){ return true }else{ return false }}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[深拷贝和浅拷贝]]></title>
<url>%2F2018%2F10%2F29%2F%E6%B7%B1%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B5%85%E6%8B%B7%E8%B4%9D%2F</url>
<content type="text"><![CDATA[知识点:浅拷贝:a复制b,b变a也变了;深拷贝:a复制b,b变a不变。基本类型:名值存储在栈内存中;引用类型:名存栈内存中,值存堆内存中,栈内存会提供一个引用地址指向堆内存中的值。 深拷贝JSON.parse() + JSON.stringify(target) (但是如果里面有 function 和 undefined 不可用)1result = JSON.parse(JSON.stringify(target)) 浅拷贝ES6:object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Array和String相互转换]]></title>
<url>%2F2018%2F10%2F29%2FArray%E5%92%8CString%E7%9B%B8%E4%BA%92%E8%BD%AC%E6%8D%A2%2F</url>
<content type="text"><![CDATA[Array 转 String、String 转 Array、字符串Array转换成数字Array Array 转 StringString()1234String([1,2,3])// "1,2,3"String(['1', '2', '3'])// "1,2,3" .toString()12[1,2,3].toString()// "1,2,3" .join()1234[1,2,3].join(",")// "1,2,3"[1,2,3].join("")// "123" String 转 Array.split()123456789101112131415// 一整个字符串"123".split("") // ["1", "2", "3"] // 字符串中内容逗号分隔"1,2,3".split(",")// ["1", "2", "3"] "1,2,3".split("")// ["1", ",", "2", ",", "3"]// 字符串中内容逗号分隔,逗号有空格// 直接用split(","),打印除了数字前会有空格,所以添加replace()去掉全部空格"1, 2, 3".replace(/\s+/g,"").split(",")// ["1", "2", "3"] Array.from()123456789Array.from('foo')// ["f", "o", "o"]// 将arguments 的值提出来。即,将类数组转为真数组。function f() { return Array.from(arguments);}f(1, 2, 3)// [1, 2, 3] Array.prototype.slice.call()12345678Array.prototype.slice.call('hello', 0, 2)// ["h", "e"]function f() { return Array.prototype.slice.call(arguments);}f(1, 2, 3)// [1, 2, 3] .match()12'hello'.match(/l/g)// ["l", "l"] 扩展运算符扩展运算符可以将字符串转为真正的数组。12[...'hello']// [ "h", "e", "l", "l", "o" ] 字符串 Array 转换成数字 Array如: ['1','2','3']=>[1,2,3] JSON.parse() + String()12JSON.parse('['+ String(['1','2','3']) + ']')// [1, 2, 3] .map()12['1','2','3'].map(Number)// [1, 2, 3] Object 转 Array1234var obj = {one: 'hello', two: 'world'}var arr = Object.keys(obj)arr = arr.map(function(i){return obj[i]})// ["hello", "world"] 伪数组 转 数组Array.prototype.slice.call(arguments)Array.from(arguments)[…arguments]123456function foo() { console.log(Array.prototype.slice.call(arguments)) console.log(Array.from(arguments)) console.log([...arguments]) }foo(1,2,3,4)]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS水平布局各栏高度自动相等]]></title>
<url>%2F2018%2F10%2F16%2FCSS%E6%B0%B4%E5%B9%B3%E5%B8%83%E5%B1%80%E5%90%84%E6%A0%8F%E9%AB%98%E5%BA%A6%E8%87%AA%E5%8A%A8%E7%9B%B8%E7%AD%89%2F</url>
<content type="text"><![CDATA[CSS水平布局各栏高度自动相等 1 table 实现效果预览1234567891011121314.container { display: table; width: 100%}.left { display: table-cell; background: rgb(222, 222, 248); width: 20%;} .right { display: table-cell; background: rgb(248, 204, 204); width: 80%;} 2 flex 实现效果预览12345678910111213.container { display: flex; width: 100%}.left { flex: 1; background: rgb(222, 222, 248); width: 20%;}.right { background: rgb(248, 204, 204); width: 80%;} 2 grid 实现效果预览1234567891011.container { display: grid; grid-template-columns: 20% 80%; grid-template-rows: auto; }.left { background: rgb(268, 222, 248);}.right { background: rgb(248, 204, 204);} PS: 一个关于grid的练习 shttp://js.jirengu.com/zisinokipu/8/edit]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS一些零碎知识整理]]></title>
<url>%2F2018%2F10%2F01%2FCSS%E4%B8%80%E4%BA%9B%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[文本溢出省略、内容超出范围部分滚动、清除滚动条、加3D效果等等 1 图形居中1234/* 置一个一个图形在页面居中,中线是左边的那条线 *//* 如果想以图形的中线居中可以用:margin-right: 图形宽度的一半;也可以用transform: translateX(-50%) */position: absolute;left: 50%; 2 让内容不超出范围,超出部分产生滚动12overflow: auto;height: 100%; 3 清除滚动条12overflow-y: hidden;overflow-x: hidden; 4 加3D效果1234/* 父元素加 */perspective:2000px; /* 子元素加 */transform:rotateY(25deg); 但是这个时候,字体会变模糊。传说中是由于浏览器渲染的问题,宽高不能设置为奇数。 5 一段文本超出可视范围时隐藏超出部分并显示…1234567891011121314151617/* 单行文本溢出省略 */.textOver{ white-space:nowrap; text-overflow:ellipsis; overflow:hidden; word-break: break-all;}/* 多行文本溢出省略 */.textOver(@n:2){ overflow: hidden; text-overflow: ellipsis; word-break: break-all; display: -webkit-box; -webkit-line-clamp: @n; -webkit-box-orient: vertical;}; 6 中文词组自动换行(词组间有空格)12345678<!-- 第一种 --><div style="word-break: keep-all">美白 明目 减脂<div><!-- 第二种 --><div style="white-space: pre-wrap"> <span style="display: inline-block">美白</span> <span style="display: inline-block">明目</span> <span style="display: inline-block">减脂</span><div> white-space的其他值123456white-space: normal /* 默认。空白会被浏览器忽略。 */white-space: pre /* 空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。 */white-space: nowrap /* 文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。 */white-space: pre-wrap /* 保留空白符序列,但是正常地进行换行。 */white-space: pre-line /* 合并空白符序列,但是保留换行符。 */white-space: inherit /* 规定应该从父元素继承 white-space 属性的值。 */ word-break的其他值123word-break:break-all; /* 强制英文单词断行 */word-wrap:break-word; /* 自动换行*/word-break:keep-all; /* 只能在半角空格或连字符处换行 */ 7 去掉点击输入框时的输入框的边框1outline: none; 8 页面宽度超出屏幕的宽度可左右滑动解决办法12height: 100%;overflow: hidden]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[获取宽高位置的方法]]></title>
<url>%2F2018%2F09%2F23%2F%E8%8E%B7%E5%8F%96%E5%AE%BD%E9%AB%98%E7%9A%84%E6%96%B9%E6%B3%95%E5%8F%8A%E5%85%B6%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[获取页面宽高,滚动的位置等的方法 浏览器可视窗口宽高(像素),如存在滚动条则包括12window.innerWidthwindow.innerHeight 整个浏览器窗口的宽高(像素),包括侧边栏、窗口镶边和窗口调正边框12window.outerWidth window.outerHeight 元素的内部宽高(像素)。包括padding,不包括滚动条、margin12element.clientWidth element.clientHeight 元素的内部宽高 + padding + border + 滚动条123// 返回element.offsetWidthelement.offsetHeight 元素的X和Y坐标12element.offsetLeftelement.offsetTop 页面滚动的距离12pageXOffsetpageYOffset 使用滚动条的情况下为了适应视口中所用内容所需的最小宽高123// 包含元素的内边距,但不包括边框,外边距或滚动条。还可以包括伪元素的宽高。element.scrollWidthelement.scrollHeight 元素的滚动条的位置12element.scrollLeft element.scrollTop ps:如果想让一个元素的溢出部分随着页面滚动,可设置element.scrollTop = element.scrollHeight 获取元素上下左右边距使用getClientRects()或getBoundingClientRect()12345var element=document.getElementById(‘box’); // 获取元素box.getBoundingClientRect().top // 元素上边距离页面上边的距离box.getBoundingClientRect().right // 元素右边距离页面左边的距离box.getBoundingClientRect().bottom // 元素下边距离页面上边的距离box.getBoundingClientRect().left// 元素左边距离页面左边的距离]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[微信小程序--数据处理]]></title>
<url>%2F2018%2F09%2F12%2F%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F-%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86%2F</url>
<content type="text"><![CDATA[微信小程序获取数据、修改数据、添加数据、删除数据的处理方式 1 获取数据123456789Page({ data: { inputText: 'hi', }, //页面载入时打印inputText的值 onLoad: function () { console.log(this.data.inputText) }}) 2 修改数据123456789Page({ ... //页面载入时修改inputText的值为hello onLoad: function () { this.setData({ inputText: 'hello' }) }}) 修改data中子对象的属性值1234567891011121314Page({ data: { userInfo: { avatarUrl: ' ' } }, //页面载入时打印inputText的值 onLoad: function () { let avatarUrl = 'userInfo.avatarUrl' this.setData({ [avatarUrl]: tempFilePaths }) }}) 3 增加数据1234567891011Page({ data: { array: ['1','2'], }, onLoad: function () { this.data.array= this.data.array.concat('3','4'); this.setData({ array: this.data.array }) }}) 4 删除数据123456789Page({ ... onLoad: function () { this.data.array.splice(Index,1); this.setData({ array: this.data.array }) }})]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>微信小程序</tag>
</tags>
</entry>
<entry>
<title><![CDATA[微信小程序--通信]]></title>
<url>%2F2018%2F09%2F11%2F%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F-%E9%80%9A%E4%BF%A1%2F</url>
<content type="text"><![CDATA[最近写微信翻译小程序过程中总结的几种常用的通信方式 1 页面与页面之间通信全局变量(app.globalData)1234567891011121314//app.jsApp({ globalData: { name: 'nola' }, ...})//index.jsconst app = getApp()Page({ onLoad: function (options) { console.log(app.globalData.name) }}) 本地缓存传递数据(wx.setStorageSync)123456789101112131415161718192021//a.js储存数据...Page({ xxxx: function () { let history = wx.getStorageSync('history') || [] //有则获取无则设为空数组 history.unshift({ aaaa: this.data.aaaa}) //添加数据 wx.setStorageSync('history', history) //储存 }})...//b.js获取数据...Page({ data: { history: [] } onShow: function () { this.setData({ history: wx.getStorageSync('history')}) }})... url传参12345//a.wxml<view> <navigator url="./about/index?from=first">first</navigator> <navigator url="./about/index?from=seconed">seconed</navigator></view> 1234//b.jsxxxx: function (e) { console.log(this.options.form) //可以判断点击的是first或seconed} 2 组件与组件之间全局变量(app.globalData)或 本地缓存传递数据(wx.setStorageSync)]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>微信小程序</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Easy LESS插件+Less将less文件转为wxss或acss文件]]></title>
<url>%2F2018%2F09%2F03%2FEasy-LESS-Less%E5%B0%86less%E6%96%87%E4%BB%B6%E8%BD%AC%E4%B8%BAwxss%E6%88%96acss%E6%96%87%E4%BB%B6%2F</url>
<content type="text"><![CDATA[这几天开始尝试写小程序,因原生css不支持嵌套,写起来结构很不清晰,所以考虑用less预处理来写。less格式转wxss格式是通过vscode的Easy LESS这款插件,通过简单的配置后可以自由转换格式如wxss、acss 所用工具Less(下载:命令行中输入npm install -g less)vscode插件Easy LESS(下载:在vscode插件中下载) 配置建一个文件夹.vscode.vscode文件夹下建一个叫settings.json的文件settings.json中配置内容如下:12345{ "less.compile": { "outExt": ".wxss" }} 建一个.less格式的文件,保存,同一个文件夹即会出现一个.wxss的文件支付宝小程序的样式格式.acss同理]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Cookie和登录注册]]></title>
<url>%2F2018%2F08%2F19%2FCookie%E5%92%8C%E7%99%BB%E5%BD%95%E6%B3%A8%E5%86%8C%2F</url>
<content type="text"><![CDATA[为什么会存在Cookie?因为 HTTP协议 是无状态的,即 服务器 不知道用户上一次做了什么,这严重阻碍了 交互式Web应用程序 的实现。Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器 会话中的状态。 Cookie 是什么?参考自方方在知乎的回答Cookie 是什么 Cookie 是浏览器访问服务器后,服务器传给浏览器的一段数据。 浏览器需要保存这段数据,不得轻易删除。 此后每次浏览器访问该服务器,都必须带上这段数据。 Cookie 作用是什么? 识别用户身份 记录历史 Cookie的缺陷是什么? Cookie会被附加在每个HTTP请求中,所以无形中增加了流量。 由于在HTTP请求中的Cookie是明文传递的,所以存在安全性成问题,除非用 HTTPS 。(Cookie会包含了一些敏感消息:用户名、计算机名、使用的浏览器和曾经访问的网站。) Cookie的大小限制在4KB左右,对于复杂的存储需求来说是不够用的。 Cookie替代?在与服务器传输数据时,通过在地址后面添加唯一 查询串 ,让服务器识别是否合法用户,也可以避免使用Cookie 待添加~~~]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Browser</tag>
</tags>
</entry>
<entry>
<title><![CDATA[几种常用js方法整理]]></title>
<url>%2F2018%2F08%2F12%2F%E5%87%A0%E7%A7%8D%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[splice()、push()、concat()、split()、indexOf()、map()、join()、forEach()、reduce()、filter()、substr()、substring() splice()splice()通过删除现有元素和/或添加新元素来修改数组,并以数组返回原数组中被修改的内容。array.splice(start, deleteCount, item)其中:start:指定修改的位置deleteCount:删除的个数item:添加进数组的元素,可多个(如无则删除数组元素)123456789101112131415var array = [1, 2, 4]// array初始值统一,就不一加了array.splice(2, 0, 3) // 第二位开始删除0个元素,在指数2处插入3,其他元素后移。因无修改返回[]// array为[1, 2, 3, 4]array.splice(2, 1, 3) // 第二位开始删除1个元素,在指数2处插入3,其他元素后移。修改了原数组[4]// array为[1, 2, 3] array.splice(2, 1)// 若没有第三个参数则表示只删除 // array为[1,2] slice()slice()返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。使用 Array.prototype.slice.call(伪数组) 可将伪数组转为真正的数组123var array = [1, 2, 3]array.slice(0, 2) // 【1,2】 push()push()可将一个或多个元素添加到数组的末尾,并返回新数组的长度,改变原数组。12345var a=['a','b']a.push('c','d')//4a//(4)["a", "b", "c", "d"] unshift()返回值也是数组长度,它的作用是把元素添加到数组开始的位置,改变原数组。pop()返回值是删除的元素(删除最后一个),改变原数组。shift()返回值是删除的元素(删除最第一个),改变原数组。 concat()concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。不会修改原数组。123var a=['a','b']a.concat('c','d') //(4) ["a", "b", "c", "d"]a //(2)["a", "b"] concat() 也可以用于在字符串后添加元素123var a = "123"a.concat('4') // "1234"a // "123" push()与 concat()区别concat()和push()都可以合并数组push() 遇到数组参数时,把整个数组参数作为一个元素;而 concat() 则是拆开数组参数,一个元素一个元素地加进去。push() 直接改变当前数组; concat() 不改变当前数组。此外,push() 只能用于数组,concat() 还能用于合并字符串。123var a='hello'a.concat('-nola')//"hello-nola" split()split() 用于把一个字符串分割成字符串数组123456789var str="How are you doing today?"var n=str.split(" ")//"How", "are", "you", "doing", "today?"]var n=str.split("")//["H", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", " ", "d", "o", "i", "n", "g", " ", "t", "o", "d", "a", "y", "?"]var n=str.split(" ",3)//["How", "are", "you"]var n=str.split("o")//["H", "w are y", "u d", "ing t", "day?"] indexOf()indexOf() 要检索的字符串值没有出现,则该返回 -11234var str="Hello world!"var n=str.indexOf("Hello") //0var n=str.indexOf("world") //6var n=str.indexOf("World") //-1 不匹配返回-1 将indexOf()匹配的所有值的index存进一个数组123456var newArr = [];var index= arr.indexOf("false");while (index!= -1) { newArr.push(index); index= arr.indexOf("false", index + 1);} map()map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果 join()join() 方法将一个数组(或一个 类数组对象 )的所有元素连接成一个字符串并返回这个字符串 forEach()forEach()遍历12345arr.forEach((node,index,arr)=>{ // node就是每一项的元素 // index 就是每一项的下标 // arr 就是这个数组}) reduce()reduce() 把数组的值通过设置参数运算,合并成一个值。12345var a=[1,2,3,4,5];a.reduce(function(a,b){ return a+b})//15 filter()filter()把符合的条件的值添加到新数组,原来的数组不变12345var a=[1,2,3,4,5];a.filter(function(value,index,array){ return value>3})//[4, 5] substr()substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。123456//stringObject.substr(start,length)var str="Hello world!"var n=str.substr(3)//lo world!var n=str.substr(3,7)//lo worl 从3下标即第四位数开始,后面的7位数(包括空格) substring()substring() 方法用于提取字符串中介于两个指定下标之间的字符实现代码在HTML页面一个一个打出的效果123//html<pre id="code"></pre> <!-- pre标签目的:代码在html中显示时能正常换行 --> 123456789//jslet n = 0let timer = setInterval(()=>{ n += 1 code.innerText = result.substring(0,n)},100)let result= ` /*你好,我是Nola*/` Array.from()Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例Array.from(obj, mapFn, thisArg) ,若添加可选函数则相当于:Array.from(obj).map(mapFn, thisArg)其中:obj:想要转换成数组的伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)或可迭代对象 (可以获取对象中的元素,如 Map和 Set 等))mapFn:可选,如果指定了该参数,新数组中的每个元素会执行该回调函数thisArg:可选,执行回调函数 mapFn时 this 对象12345678910111213141516171819202122232425Array.from('foo')// ["f", "o", "o"]Array.from([1, 2, 3], x => x + x))// [2, 4, 6]Array.from({ length: 2 })// [undefined, undefined]Array.from({length:2}).fill(null) // 相当于 new Array(2).fill(null)// [null, null]// 一道题目let obj = {1:222, 2:123, 5:888}Array.from({length:5}, (item,i) => obj[i+1]||null) // Array.from({length:5}).map((item,i) => data[i+1]||null);// [222, 123, null, null, 888]obj.length = 5let _obj = Array.from(obj).slice(1)// [222, 123, undefined, undefined, 888]obj.length = 5obj[Symbol.iterator] = Array.prototype[Symbol.iterator]let _obj = [...obj].slice(1)// [222, 123, undefined, undefined, 888]]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[自定义data-*属性]]></title>
<url>%2F2018%2F08%2F09%2F%E8%87%AA%E5%AE%9A%E4%B9%89data-%E5%B1%9E%E6%80%A7%2F</url>
<content type="text"><![CDATA[自定义data-*属性,HTML标签上添加以 “data-“开头的属性即可12//HTML中<li id="tab" data-role="tabbbb" data-view="#rec-view" >推荐</li> 读取的时候是通过dataset对象,使用”.”来获取属性,需要去掉data-前缀,连字符需要转化为驼峰命名123//js中读取let tab = document.querySelector('#tab')tab.dataset.role]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTML/DOM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[将数据保存到本地localStorage]]></title>
<url>%2F2018%2F08%2F05%2F%E5%B0%86%E6%95%B0%E6%8D%AE%E4%BF%9D%E5%AD%98%E5%88%B0%E6%9C%AC%E5%9C%B0LocalStorage%2F</url>
<content type="text"><![CDATA[localStorage 类似于 sessionStorage 不同的是sessionStorage在页面被关闭时数据存储会被清除,而localStorage 是无期限的 1 什么是localStorage?只读的localStorage 允许你访问一个 Document 的远端(origin)对象 Storage12345678//增加数据localStorage.setItem(``);//读取数据localStorage.getItem(``)//移除数据localStorage.removeItem(``)// 移除所有localStorage.clear(); 2 将数据保存到本地localStorage中12345678// 页面刷新后依然在本地保留数据window.onbeforeunload = ()=>{ //页面刷新时调用 let dataString = JSON.stringify(this.todoList) //将todoList对象转化为JSON字符串 dataString window.localStorage.setItem('myTodos', dataString) //通过window.localStorage添加dataString数据到"myTodos"中}let oldDataString = window.localStorage.getItem('myTodos') //通过window.localStorage读取"myTodos"中的数据let oldData = JSON.parse(oldDataString) //将读取到的JSON字符串解析JSON字符串this.todoList = oldData || [] //把数据赋值给todoList对象 但是,beforeunload 事件里面的所有请求都发不出去,会被浏览器取消。所以,beforeunload事件里不能将数据存到 LeanCloud的。 3 window.onbeforeunload当窗口即将被 卸载(关闭) 时会触发该事件,此时页面文档依然可见,且该事件的默认动作可以被 取消onunload,onbeforeunload都是在刷新或关闭时调用,可以在脚本中通过window.onunload来指定或者在里指定。区别在于onbeforeunload在onunload之前执行,它还可 以阻止onunload的执行。onbeforeunload是正要去服务器读 取新的页面时调用,此时还没开始读取;而onunload则已经从服务器上读到了需要加载的新的页面,在即将替换掉当前页面时调用。onunload是无 法阻止页面的更新和关闭的。而 onbeforeunload可以做到。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Browser</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS垂直水平居中方法汇总]]></title>
<url>%2F2018%2F08%2F02%2FCSS%E5%9E%82%E7%9B%B4%E6%B0%B4%E5%B9%B3%E5%B1%85%E4%B8%AD%E6%96%B9%E6%B3%95%E6%B1%87%E6%80%BB%2F</url>
<content type="text"><![CDATA[用思维导图整理了CSS垂直水平居中的一些方法垂直居中思维导图 1 垂直居中 父元素高度确定的单行文本:height (元素高度)= line-height(行高) 父元素高度确定的多行文本: 给子元素的position:relative; top: 50%; translateY(-50%) 父元素添加伪元素:before,设置伪元素height: 100%; display: inline-block; vertical-align:middle;使得子元素实现垂直居中。 插入 table (插入方法和水平居中一样),然后设置 vertical-align:middle 先设置 display:table-cell 再设置 vertical-align:middle 父元素高度不确定:父元素设置 position:relative,子元素设置 position:absolute ; top: 50%; translateY(-50%) 万能的display: flex; 2 水平居中 内联元素居中: 给父元素设置 text-align:center 块级元素居中: 定宽块级元素居中:margin:0 auto(前提是已经为元素设置了适当的 width 宽度,否则块级元素的宽度会被拉伸为父级容器的宽度) 不定宽块级元素居中: 给该元素设置 display:inline,使其变成内联元素然后在父元素中设置 text-align:center 给该元素设置 display:inline-block,然后给父元素设置 text-align:center。(加了inline-block,下面会出现一个空隙,如果比较介意这个空隙,可以加vertical-align: top; 消除) 给父元素设置 position:relative,子元素设置 position:absolute ; left: 50%; margin-left: weight一半值的负数; 将要显示的元素加入到 table 标签当中,然后为 table 标签设置“左右margin”值为“auto”来实现居中。(为什么加入table标签? 是利用table标签的长度自适应性—即不定义其长度也不默认父元素body的长度(table其长度根据其内文本长度决定),因此可以看做一个定宽度块元素,然后再利用定宽度块状居中的margin的方法,使其水平居中。) 万能的display: flex; 3 页面垂直水平居中 让子元素在页面居中,可设置父元素:先设置display: flex;然后 justify-content: center;此时子元素左右居中,再align-items: center; 垂直居中。 给父元素设置 position:relative,子元素设置 position: absolute; top: 50%; left: 50%,最后使用负向 margin 实现水平和垂直居中。 如果宽高固定:margin 的值为宽高(具体的宽高需要根据实际情况计算 padding)的一半的负数 如果宽高不固定:transform: translate(-50%, -50%);]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[正则相关知识点整理]]></title>
<url>%2F2018%2F07%2F29%2F%E6%AD%A3%E5%88%99%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E7%82%B9%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[正则的符号及其含义及常用方法的整理 1 符号及其含义123456* 重复0+次+ 重复1+次? 重复0或1次{n} 重复n次{n,} 重复n+次{n,m} 重复n-m次 2 正则一些常用方法test()123456789101112131415new RegExp('Nola','i') // /Nola/i/No/.test('Nola') // true//匹配数字/[0123456789]/.test('3') // true/[0-9]/.test('3') // true/[^0-9]/.test('3') // false 正则里[]中的^是取反的意思,非/\d/.test('3') // true//匹配字母数字/[a-zA-Z]/.test('abc123') // true/\w/.test('abc3') // true//其他/./.test('abc123') // true 点可以匹配任何东西,除了换行符号 search()类似于indexOf()12'Nola'.search(/o/) // 1'Nola'.search(/x/) // -1 不匹配返回-1,类似于indexOf() match()可以将字符串转化成数组的形式123456'Date 2018-08-07 2018-09-15'.match(/\d{4}-\d{2}-\d{2}/)// ["2018-08-07"]'Date 2018-08-07 2018-09-15'.match(/\d{4}-\d{2}-\d{2}/g)// ["2018-08-07", "2018-09-15"]'1 comment 1,23 comments'.match(/[\d,]+ comments?/g)// ["1 comment","1,23 comment"] //如果不加g只会返回第一个 string.matches()这个方法主要是返回是否匹配指定的字符串,如果匹配则为true,否则为false; replace()匹配并替换123//[ ] \ ^ $ . | ? * + ( )需要转义,[] 之间的特殊字符一般不需要转义'2018.08.07'.replace(/\./g, '-')// "2018-08-07" 输入框只能输入数字123onkeyup=“this.value=this.value.replace(/[^\d]/g,’’) “ onafterpaste=“this.value=this.value.replace(/[^\d]/g,’’) “//onafterpaste作用:防止用户从其它地方copy内容粘贴到输入框 输入框只能输入字母和下横线的正则表达式12onkeyup="this.value=this.value.replace(/[^_a-zA-Z]/g,'')" onpaste="this.value=this.value.replace(/[^_a-zA-Z]/g,'')" 输入框只能输入字母数字和下横线的正则表达式123456//第一种方法onkeyup="this.value=this.value.replace(/[^\w]/g,'')" onpaste="this.value=this.value.replace(/[^\w]/g,'')"//第二种方法onkeyup="this.value=this.value.replace(/[\W]/g,'')" onpaste="this.value=this.value.replace(/[\W]/g,'')" 测试是否是数字/替换非数字部分12/^[0-9]*$/.test(num)) //测试输入的是不是数字num=num.replace(/[^0-9]+/ig,"") //替换掉不是数字的部分 3 贪婪模式和懒惰模式贪婪模式 /'.*'/g中.* 是贪婪的,会尽可能的匹配多。包括匹配的两者之间的12"there is 'Lola' and 'Nola'".match(/'.*'/g)// ["'Lola' and 'Nola'"] 懒惰模式给 .* 设置懒惰模式成 .*?。其中?指0或1次12"there is 'Lola' and 'Nola'".match(/'.*?'/g)// ["'Lola','Nola'"] 4 分组捕获和排除分组分组捕获模式即一对括号 () 包围的模式, []是定义匹配的字符范围12345'127.0.0'.match(/(\d{1,3}\.){2}\d{1,3}/g)// ["127.0.0"] ()分组即让(\d{1,3}\.)成为一个整体{3}即出现3次//$1代表第一个分组捕获的内容'I am Nola from ShangHai'.replace(/I am (\w{4}) from ShangHai/,'我是来自上海的 $1')// "我是来自上海的 Nola" 排除分组在分组添加 ?: 可进行排除如(?:\d+)$/)。() 中的 | 表示 或。Ps:正则的 m 标记 /…/m 表示多行模式,它会影响 ^ 和 $ 的表现。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>RegExp</tag>
</tags>
</entry>
<entry>
<title><![CDATA[XSS攻击和CSRF攻击]]></title>
<url>%2F2018%2F07%2F20%2FXSS%E6%94%BB%E5%87%BB%E5%92%8CCSRF%E6%94%BB%E5%87%BB%2F</url>
<content type="text"><![CDATA[整理了XSS攻击和CSRF攻击的攻击原理以及如何预防这类攻击 XSS 攻击及预防XSS(Cross SiteScript跨站脚本攻击)攻击原理为:攻击者向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。会造成会话劫持,信息泄露等风险 XSS 攻击方式: 通过修改浏览器URL导致脚本被注入到页面(会被 chrome 浏览器自动防御攻击) 通过输入框将脚本代码注入数据库(可通过 xss 库的白名单过滤防御) XSS 攻击预防方法: 使用innerText替代innerHTML 使用 innerHTML 时进行字符过滤,如:< => &lt、> => &gt、& => &amp、‘ => &#39、‘ => &quot 使用 CSP (Content Security Policy内容安全性政策) Cookie 设置 HTTP Only(cookie只能通过服务器端修改,通过js脚本将无法读取到cookie信息,防止用户身份验证信息被盗取) CSRF 攻击及预防CSRF(Cross-site request forgery跨站请求伪造)源于Web的隐式身份验证机制,其攻击原理为: 用户打开浏览器,访问受信任网站 A ,输入用户名和密码请求登录网站 A 用户信息通过验证后,网站 A 产生 Cookie 信息并返给浏览器,此时用户登录网站 A 成功,可以正常发送请求到网站 A 用户未退出网站 A 之前,在同一浏览器中,打开一个 TAB 页访问网站 B 网站 B 接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点 A 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带 Cookie 信息,向网站 A 发出请求 网站 A 并不知道该请求其实是由 B 发起的,所以会根据用户的 Cookie 信息以的权限处理该请求,导致来自网站 B 的恶意代码被执行 CSRF 攻击预防方法: Get 请求不用于对数据进行修改 重要数据交互采用 POST(并不安全,伪造一个form 表单即可破解) 验证 HTTP Referer(来源地址) 字段(并不安全,Referer可以被更改) 在 HTTP 头中自定义属性并验证 请求时带上验证信息,如验证码、Token 接口设置禁止跨域]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Security</tag>
</tags>
</entry>
<entry>
<title><![CDATA[几种经典排序算法原理]]></title>
<url>%2F2018%2F07%2F19%2F%E5%87%A0%E7%A7%8D%E7%BB%8F%E5%85%B8%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86%2F</url>
<content type="text"><![CDATA[几种经典排序算法的原理参考自:十大经典排序算法 快速排序 从数列中挑出一个元素,称为 “基准” 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区操作 递归地把小于基准值元素的子数列和大于基准值元素的子数列排1234567891011121314151617181920212223242526272829303132function quickSort(arr, left, right) { var len = arr.length, partitionIndex, left = typeof left != 'number' ? 0 : left, right = typeof right != 'number' ? len - 1 : right if (left < right) { partitionIndex = partition(arr, left, right) quickSort(arr, left, partitionIndex-1) quickSort(arr, partitionIndex+1, right) } return arr}function partition(arr, left ,right) { // 分区操作 var pivot = left, // 设定基准值 index = pivot + 1 for (var i = index; i <= right; i++) { if (arr[i] < arr[pivot]) { swap(arr, i, index) index++ } } swap(arr, pivot, index - 1); return index-1}function swap(arr, i, j) { var temp = arr[i] arr[i] = = arr[j] arr[j] = temp} 冒泡排序 比较相邻的元素。如果第一个比第二个大,就交换他们两个 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数 针对所有的元素重复以上的步骤,除了最后一个 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较12345678910111213function bubbleSort(arr) { var len = arr.length for (var i = 0; i < len - 1; i++) { for (var j = 0; j < len - 1 - i; j++) { if (arr[j] > arr[j+1]) { // 相邻元素两两对比 var temp = arr[j+1] // 元素交换 arr[j+1] = arr[j] arr[j] = temp } } } return arr} 选择排序 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾 重复第二步,直到所有元素均排序完毕12345678910111213141516function selectionSort(arr) { var len = arr.length var minIndex, temp for (var i = 0; i < len - 1; i++) { minIndex = i for (var j = i + 1; j < len; j++) { if (arr[j] < arr[minIndex]) { // 寻找最小的数 minIndex = j // 将最小数的索引保存 } } temp = arr[i] arr[i] = arr[minIndex] arr[minIndex] = temp } return arr} 基数排序–根据键值的每位数字来分配桶 拿10个桶,多次使用。首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶。装完后拿出来,接着再次入桶,不过这次以十位数的数字为准123456789101112131415161718192021222324var counter = []function radixSort(arr, maxDigit) { var mod = 10 var dev = 1 for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) { for(var j = 0; j < arr.length; j++) { var bucket = parseInt((arr[j] % mod) / dev) if(counter[bucket]==null) { counter[bucket] = [] } counter[bucket].push(arr[j]) } var pos = 0 for(var j = 0; j < counter.length; j++) { var value = null if(counter[j]!=null) { while ((value = counter[j].shift()) != null) { arr[pos++] = value } } } } return arr} 计数排序–每个桶只存储单一键值 将输入的数据值转化为键存储在额外开辟的数组空间中12345678910111213141516171819202122function countingSort(arr, maxValue) { var bucket = new Array(maxValue+1), sortedIndex = 0 arrLen = arr.length, bucketLen = maxValue + 1 for (var i = 0; i < arrLen; i++) { if (!bucket[arr[i]]) { bucket[arr[i]] = 0; } bucket[arr[i]]++ } for (var j = 0; j < bucketLen; j++) { while(bucket[j] > 0) { arr[sortedIndex++] = j bucket[j]-- } } return arr} 桶排序–每个桶存储一定范围的数值 桶排序是计数排序的升级版。它利用了函数的映射关系 12345678910111213141516171819202122232425262728293031323334353637383940function bucketSort(arr, bucketSize) { if (arr.length === 0) { return arr } var i var minValue = arr[0] var maxValue = arr[0] for (i = 1; i < arr.length; i++) { if (arr[i] < minValue) { minValue = arr[i] // 输入数据的最小值 } else if (arr[i] > maxValue) { maxValue = arr[i] // 输入数据的最大值 } } //桶的初始化 var DEFAULT_BUCKET_SIZE = 5 // 设置桶的默认数量为5 bucketSize = bucketSize || DEFAULT_BUCKET_SIZE var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1 var buckets = new Array(bucketCount) for (i = 0; i < buckets.length; i++) { buckets[i] = [] } //利用映射函数将数据分配到各个桶中 for (i = 0; i < arr.length; i++) { buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]) } arr.length = 0 for (i = 0; i < buckets.length; i++) { insertionSort(buckets[i]) // 对每个桶进行排序,这里使用了插入排序 for (var j = 0; j < buckets[i].length; j++) { arr.push(buckets[i][j]) } } return arr} 插入排序原理:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)1234567891011121314function insertionSort(arr) { var len = arr.length var preIndex, current for (var i = 1; i < len; i++) { preIndex = i - 1 current = arr[i] while(preIndex >= 0 && arr[preIndex] > current) { arr[preIndex+1] = arr[preIndex] preIndex-- } arr[preIndex+1] = current } return arr} 希尔排序 也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法 先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序 待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序123456789101112131415161718function shellSort(arr) { var len = arr.length, temp, gap = 1 while(gap < len/3) { //动态定义间隔序列 gap =gap*3+1 } for (gap; gap > 0; gap = Math.floor(gap/3)) { for (var i = gap; i < len; i++) { temp = arr[i] for (var j = i-gap; j >= 0 && arr[j] > temp; j-=gap) { arr[j+gap] = arr[j] } arr[j+gap] = temp } } return arr} 堆排序堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素二叉堆一般分为两种:最大堆和最小堆。最大堆:堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆,因此,最大堆中的最大元素值出现在根结点(堆顶) 堆排序就是把堆顶的最大数取出 将剩余的堆继续调整为最大堆 剩余部分调整为最大堆后,再次将堆顶的最大数取出,再将剩余部分调整为最大堆 这个过程持续到剩余数只有1个时结束1234567891011121314151617181920212223242526272829303132333435363738394041424344var len // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量function heapSort(arr) { buildMaxHeap(arr) for (var i = arr.length-1; i > 0; i--) { swap(arr, 0, i) len-- heapify(arr, 0) } return arr;}function buildMaxHeap(arr) { // 建立大顶堆 len = arr.length for (var i = Math.floor(len/2); i >= 0; i--) { heapify(arr, i) }}function heapify(arr, i) { // 堆调整 var left = 2 * i + 1, right = 2 * i + 2, largest = i if (left < len && arr[left] > arr[largest]) { largest = left } if (right < len && arr[right] > arr[largest]) { largest = right } if (largest != i) { swap(arr, i, largest) heapify(arr, largest) }}function swap(arr, i, j) { var temp = arr[i] arr[i] = arr[j] arr[j] = temp} 归并排序 把原始数组分成若干子数组,对每一个子数组进行排序,继续把子数组与子数组合并,合并后仍然有序,直到全部合并完,形成有序的数组 始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间123456789101112131415161718192021222324252627282930function mergeSort(arr) { // 采用自上而下的递归方法 var len = arr.length if(len < 2) { return arr } var middle = Math.floor(len / 2), left = arr.slice(0, middle), right = arr.slice(middle) return merge(mergeSort(left), mergeSort(right))}function merge(left, right) { var result = [] while (left.length && right.length) { if (left[0] <= right[0]) { result.push(left.shift()) } else { result.push(right.shift()) } } while (left.length) result.push(left.shift()) while (right.length) result.push(right.shift()) return result}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Arithmetic</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Vue相关知识整理]]></title>
<url>%2F2018%2F07%2F18%2FVue%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[整理的在使用vue的过程中遇到的一些零碎的容易忘记的知识点 Vuevue的computed属性和methods区别 computed是响应式的,methods并非响应式。 调用方式不一样,computed定义的成员像属性一样访问,methods定义的成员必须以函数形式调用。 computed是带缓存的,只有其引用的响应式属性发生改变时才会重新计算,而methods里的函数在每次调用时都要执行。 computed中的成员可以只定义一个函数作为只读属性,也可以定义get/set变成可读写属性,这点是methods中的成员做不到的 Vue 的双向绑定是如何实现的?如何追踪变化?把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。vue响应式原理 父子通信组件关系可分为父子组件通信、兄弟组件通信(new Vue() 作为 eventBus)、跨级组件通信。父子组件通信的方法为:子组件通过props方法接受父组件的data数据,子组件通过$emit触发父组件的自定义事件,如:123456789101112// 父组件将数据(currentUser)和方法(logout)传递出去<template> <Todo v-else @logout="logout" :currentUser="currentUser"/></template>// 子组件接收数据和方法props : ['currentUser'],methods: { logout() { //注册 _user this.$emit("logout") }} on和emit的事件必须是在一个公共的实例上,才能触发子组件用$emit()触发事件this.$emit(‘logout’)父组件用$on()监昕子组件的事件@logout =“logout”父子通信 v-show和v-if指令的共同点和不同点v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果 生命周期生命周期,即Vue 实例从创建到销毁的过程,一共分为8个阶段。生命周期可以让我们在控制整个Vue实例时形成好的逻辑。第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子。DOM 在 mounted时渲染完成。Vue生命周期举例API — Vue.js创建前/后:beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。这个时候可以加loading事件。created阶段,vue实例的数据对象data有了,$el还没有。这个时候可以结束loading事件,也可以调用异步请求。载入前/后:beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。mounted阶段,vue实例挂载完成,data.message成功渲染。更新前/后:当data变化时,会触发beforeUpdate和updated方法。销毁前/后:beforeDestroy时可以做一个确认停止事件的确认框。执行 destroy 方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在 css在Vue.cli中怎么安装使用?Scss:可以用变量,例如($变量名称=值)可以用混合器,例如()可以嵌套Vue.cli中安装使用:12345//安装npm i -D sass-loadernpm i -D node-sass//页面引用<style lang="scss"> 什么是MVVMViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作是自动的,无需人为操作DOM。MVVM主要解决了MVC中大量的DOM 操作使页面渲染性能降低,加载速度变慢的缺陷 一些报错及解决方法 Vue空格等报错解决方法 1234567891011//注释掉webpack.base.config.js文件中的如下代码const createLintingRule = () => ({ /*test: /\.(js|vue)$/, loader: 'eslint-loader', enforce: 'pre', include: [resolve('src'), resolve('test')], options: { formatter: require('eslint-friendly-formatter'), emitWarning: !config.dev.showEslintErrorsInOverlay }*/}) Vue去掉警告 You are running Vue in development mode在js中加一句:Vue.config.productionTip=false 使用Vue时的注意事项及一些小技巧 vue实例挂载点,不能是div和html 单vue组件,template必需且只能有一个根结点 监听一个vue组件的input时加.native`@keyup.enter.native=”addTodo”` 使文本框自动获取焦点的方法: 123456789101112// html文件<input ref="input">// js文件new Vue({ ... mounted() { this.$refs['input'].focus() // this.$refs['input'].value = '' }, ...}) 让CSS只在当前组件中起作用,可以将当前组件的<style>修改为<style scoped> 渲染时莫名其妙报错可以用:key="xxx.id"解决。原因:key的值是需要是唯一识别的 避免v-if和v-for 用在一起。为什么?因为v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。只检查它一次,且不会在 v-if 为否的时候运算 v-for。 图片资源懒加载1234567// 下载$ npm install vue-lazyload --save-dev// 引入import VueLazyload from 'vue-lazyload'Vue.use(VueLazyload)// 使用<img v-lazy="/static/img/1.png"> 性能优化 频繁切换条件场景用v-show,不频繁的切换使用v-if。(v-show元素一直被渲染; v-if条件为真时才渲染) 依赖的属性值进行数值计算时使用computed,监听数据变化后执行异步或开销较大的操作时使用watch v-for比v-if优先级高,如使用在同一个标签,会意味着每个循环都会执行v-if,如果不是每个循环都需要判断,最好将v-if放在循环外面 如果内容不需要改动,可将数据Object.freeze(data)冻结起来,防止Vue通过 Object.defineProperty对数据进行劫持 由于在Vue组件内使用addEventListene等方式是不会自动销毁的,所以需要在组件销毁时手动removeEventListener移除这些事件的监听,以免造成内存泄露 图片路由资源懒加载 第三方插件按需引入,可借用babel-plugin-component插件 服务端渲染SSR,直接在服务端完成渲染,所以可以被搜索引擎爬取工具抓取(和异步获取的SPA页面相比有更好的SEO,搜索引擎爬取工具抓取不到异步返回的内容),且无需等待所有js加载完再去渲染,能加快首屏加载速度。缺点:需要Node.js环境、只支持beforCreate和created两个钩子函数、服务器负载 VuexVuex 是一个专为 Vue.js 应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,并没有任何永久存储的功能。每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。十分钟入门Vuex 如何在 Vue 组件中展示状态?一个store实例:123456789import Vue from "vue"import Vuex from "vuex"Vue.use(Vuex)const store = new Vuex.Store({ state, getters, mutations, actions, }) 在Vue的实例中,把 store 对象提供给 “store” 选项即store:store,这可以把 store 的实例注入所有的子组件,子组件能通过this.$store访问到,如:1234const app = new Vue({ el: '#app', store,}) Vuex的几种属性介绍vuex一共有5种属性,分别是State、 Getter、Mutation 、Action、 Module State Vuex就是一个仓库,仓库里面放了很多对象,state是唯一的数据源,相当于VUE中的data State里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新 通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation mapState辅助函数 Getter Getters 可以对State进行计算操作,相当于VUE中的computed,getters 可以在多组件之间复用,如果一个状态只在一个组件内使用,是可以不用getters 可以通过属性访问,如 12345computed: { doneTodosCount () { return this.$store.getters.doneTodosCount }} 可以通过方法访问(让 getter 返回一个函数,来实现给 getter 传参,如store.getters.getTodoById(2)的形式),但是需要注意的是,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。 mapGetters辅助函数 Mutation 同步,相当于VUE中的methods,但是只能是同步。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。它的回调函数(handler)接受state作为第一个参数,如handler(state,其他参数){} 不能直接调用一个 mutation handler,需要以相应的 type 调用 store.commit 方法,如: 1234567mutations: { increment (state, n) { state.count += n }}//调用store.commit('increment',额外的参数即载荷payload) 对象风格的提交方式,即直接使用包含 type 属性的对象 1234store.commit({ type: 'increment', amount: 10}) 在对象上添加新属性Vue.set(obj, 'newProp', 123)state.obj = { ...state.obj, newProp: 123 } Action异步,但是actions不能对数据,只能用commit调用mutations。参数是store,可以用{commit},然后commit(‘mutations中的方法’, 参数), this.$store.dispatch用于分发action Module在其他地方用引入123456789101112//在其他地方用引入import {mapState,mapGetters,mapMuations, mapAtions} from 'vuex'export default { computed:{ ...mapState(['increment'])//相当于将 `this.increment()` 映射为 `this.$store.commit('increment')` ...mapGetters(['']) } methods:{ ...mapMuations(['']) ...mapAtions(['']) }} 3 热重载Hot Reload把getters统一为数据的获取出口,actions统一为数据的操作入口热重载:主要在开发环境中使用;当模块内容修改时,保留Vuex数据,重载修改模块的业务逻辑;如果不用热重载,修改模块时整体刷新,数据不再保留12345678910111213141516171819202122232425262728293031323334353637//Vuex 支持在开发过程中热重载 mutation、module、action 和 getter、//对于 mutation 和模块,需要使用 store.hotUpdate() 方法import Vue from 'vue'import Vuex from 'vuex'import mutations from './mutations.js'import getters from './getters.js'import actions from './actions.js'import modulesA from './modules/a'Vue.use(Vuex)const state = { ... }const store = new Vuex.Store({ state, getters, mutations, actions, modules: { a: moduleA }}})//热重载if (module.hot) { // 使 action 和 mutation 成为可热重载模块 module.hot.accept(['./getters.js','./mutations.js', './actions.js','./modules/a'], () => { // 获取更新后的模块 // 因为 babel 6 的模块编译格式问题,这里需要加上 `.default` // 加载新模块 store.hotUpdate({ getters: require('./getters.js').default mutations: require('./mutations.js').default actions: equire('./actions.js').default modules: { a: require('./modules/a').default } }) })} Vue-routervue-router入门学习文章路由执行过程:用户点击 router-link 标签时,寻找 to 属性,to 属性和 js 中配置的路径{ path: '/home', component: Home} path 对应,找到匹配的组件,最后把组件渲染到 router-view 标签,OK。 1 $router和$route的区别是什么?都在什么时候用?this.$router:表示全局路由器对象,项目中通过router路由参数注入路由之后,在任何一个页面都可以通过此方法获取到路由器对象,并调用其push(), go()等方法; this.$route:表示当前正在用于跳转的路由器对象,可以调用其name、path、query、params等方法;12345678910111213//两条路由const routes = [ { path: '/home', component: Home }, { path: '/about', component: About }]//创建路由实例const router = new VueRouter({ routes,})//配置完成后把路由实例注入到vue根实例const app = new Vue({ router})]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Vue</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Vue-cli脚手架搭建项目]]></title>
<url>%2F2018%2F06%2F29%2F%E8%84%9A%E6%89%8B%E6%9E%B6%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%2F</url>
<content type="text"><![CDATA[创建一个简单的 vue-cli 脚手架项目 创建 vue-cli 脚手架项目 安装vue-cli :npm install vue-cli -g 使用vue初始化基于webpack的新项目:vue init webpack projectName 进入项目目录安装依赖:npm installpackage.json定义了这个项目所需要的各种模块,以及项目的配置信息,每个项目的根目录下都有,npm install命令会根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。(如果需要手动创建,可使用npm init --yes安装一个默认的package.json) 项目运行起来npm run dev 项目正常启动后,可继续安装vue的辅助工具安装项目运行所依赖的模块(dependencies 模块)时:npm install xxx —save或npm install xxx -S安装项目开发所需要的模块(devDependencies模块),如,linter, 测试库等:npm install xxx —save-dev或npm install xxx -D如安装路由:npm install vue-router --save 打包的命令npm run build,打包完成后目录下会多一个dist的文件夹 调整 src 文件目录结构在开发路径src下增加modules和pages文件夹,分别存放模块和页面有关页面的所有文件都放到同一文件夹下就近管理:1234567modules (通用 js 和通用 css 文件)pages(放页面相关文件)main.js(页面入口文件)App.vue(页面使用的组件)components(放公用组件)router(页面的路由配置)assets(页面的静态资源) 配置 webpack 文件脚手架项目中运行npm run build即可自动进行打包 设置项目小图标icobuild/webpack.dev.conf.js 文件中添加:123456new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true, favicon: './static/favicon.ico' //添加这行}) index.html 中添加:1<link rel="shortcut icon" href="static/favicon.ico" type="image/x-icon"/> 重启后项目的小图标已经设置完成:1npm run dev 自定义字体设置字体放到项目目录中,如字体名为: animal.ttf建一个 css 文件,引入字体并设置字体名:1234@font-face { font-family: animal; src: url("./animal.ttf"); } 项目中引入 css 文件:12345// css 样式中引入方式@import "./animal-font.css";// js 样式中引入方式import "./animal-font.css"; 从小程序分享的 H5(vue-cli) 页面时ios系统正常,android分享时地址出错具体如下:ios系统的分享后解析的地址:https://miniapi.yourgenex.com/h5/#/activity/eat-animal/home?token=eyJ0eXAiOiJKV1QiLCJhbGciOiandroid 系统分享后解析的地址:https://miniapi.yourgenex.com/h5?token=eyJ0eXAiOiJKV1QiLCJhbGciOi/#/activity/eat-animal/home 解决:去除 vue 的路由地址的 # 号分隔1234const router = new VueRouter({ mode: 'history', routes: [...]}) css中使用相对路径引入字体和图片后打包出错的问题解决解决:build文件夹 - utils.js文件中添加一行代码:123456789101112if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader', publicPath: '../../' // 添加这行 })s} else { return ['vue-style-loader'].concat(loaders)}// URL 以"~ "、"@"开头,"~ " 、"@"后的任何内容都会作为一个模块请求被解析,如:// url('~assets/font/wanwan.woff')// ps: @ 是在 webpack 设置的 alias Webpack打包后的文件路径不正确将config-index.js-assetsPublicPath中的/改成./即可]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Vue</tag>
</tags>
</entry>
<entry>
<title><![CDATA[RAP + Mock.js动态生成模拟数据]]></title>
<url>%2F2018%2F06%2F25%2FRAP%2BMockjs%E5%8A%A8%E6%80%81%E7%94%9F%E6%88%90%E6%A8%A1%E6%8B%9F%E6%95%B0%E6%8D%AE%2F</url>
<content type="text"><![CDATA[ps:补充一下了Mock.js的用法,以RAP不能使用的时候自己通过Mock的方式来处理接口 1 npm init npm init是用来装package.json的 npm init --yes安装一个默认的package.json 在安装一个要打包到生产环境的安装包时,你应该使用npm install —save,如果你在安装一个用于开发环境的安装包(例如,linter, 测试库等),你应该使用npm install —save-dev。 如使用如下代码,则会自动在文档中添加一个dependencies模块(这些包在生产中需要) 123$ npm install mockjs -S或者$ npm install mockjs --save 如使用如下代码,则会自动在文档中添加一个devDependencies模块(这些包用于开发和测试) 1234//安装到你项目的目录$ npm install webpack -D//全局安装 不建议用$ npm install -g webpack 2 RAP RAP 是一个 GUI (可视化)API管理工具,通过分析接口结构,动态生成模拟数据,校验真实接口正确性, 围绕接口定义,通过一系列自动化工具提升协作效率。在 RAP 中,可以定义接口的 URL、请求 & 响应细节格式等等。还提供 MOCK 服务、测试服务等工具,帮助开发团队提高开发效率。RAP使用手册 API是什么?即Application Programming Interface,应用程序编程接口 API管理工具是什么?在前后端分离的开发模式下,为了方便前后端之间接口的展现和调用,提高开发效率,为了让测试人员更好的根据接口文档进行测试,通常需要定义一份API接口文档来规范接口的具体信息,如一个请求的地址、参数、参数名称及类型含义等等。API管理工具可以帮助我们管理这些接口,现在常用 API 管理工具有 Swagger、RAP、NEI、eolinker、EasyAPI、SosoApi、Postman 等。 3 Mock.js Mock.js 用于生成随机数据,拦截 Ajax 请求。Mock.js示例 当RAP的接口不能使用的时候要怎么处理?12345678910111213//下载Mock,并在页面引入Mockimport Mock from mock.jslet Random = Mock.Randomlet data = Mock.mock({ "lists|6": [ { "id|10000-99999": 1, "img": "@image(178x178,@color)", "name": "@ctitle", "price|1-100.2-2": 1 } ]})]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Other</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HTTP状态码及其含义]]></title>
<url>%2F2018%2F06%2F21%2FHTTP%E7%8A%B6%E6%80%81%E7%A0%81%E5%8F%8A%E5%85%B6%E5%90%AB%E4%B9%89%2F</url>
<content type="text"><![CDATA[HTTP一些常见的状态码及其含义 1 2XX 成功请求被正常处理 200 OK表示从客户端发来的请求在服务器端被正常处理 204 No Content表示服务器接收的请求以成功处理,但没有资源可返回,即:响应报文中不含实体的主体部分 206 Partial Content表示客户端进行了范围请求且服务器成功执行了这部分的GET请求,响应报文中包含由Content_Range指定范围的实体内容“Content_Range为请求首部的一种类型,后面的随笔会讲到” 2 3XX 重定向服务器需要执行某些特殊处理以正确处理请求(即URI地址或者资源的缓存的资源有效时间过期) 301 Moved Permanently永久性重定向:表示请求的资源已被分配了新的URI,以后应使用资源现在的URI,如果已经保存了书签,这时候应该按照Location首部提示的URI重新保存 302 Found临时性重定向:表示请求的资源已被分配到了新的URI,希望(本次)能使用新的URI访问 303 See Other表示请求对应的资源存在另一个URI,应该使用GET方法定向获取请求的资源PS:当301、302、303响应状态码返回,几乎所有浏览器都会把POST改成GET,并删除请求报文内的主体,之后请求自动再次发送301、302标准禁止将POST改为GET,但实际中都会允许这么做~~~GG 304 Not Modified表示客户端发送得附带条件的请求时,服务器运行请求访问,但未满足条件的情况,304返回时,不包含任何响应的主体部分附带条件:采用GET方法的请求报文中包含If-……条件的任一首部,后面的随笔中介绍 307 Temporary Redirect临时重定向:禁止将POST转换为GET,该状态码会严格遵守浏览器标准 3 4XX客户端错误4XX的响应结果表明客户端是发生错误的原因所在 400 Bad Ruquest请求报文存在语法错误 401 Unauthorized发送的请求需要有通过http认证(BASIC认证、DIGEST认证)的认证信息PS:若之前已经进行了一次请求,则表示用户认证失败返回含有401的响应必须包含一个适用于被请求资源的WWW-Authenticate首部用来质询用户信息 403 Forbidden对请求资源的访问被服务器拒绝(服务端没有必要给出拒绝的详细理由,如果想做说明,可在实体主体部分对原因进行描述)举例:未获得文件系统的访问授权、访问权限出现某些问题等 404 Not Found服务器上无法找到请求的资源 4 5XX服务器错误服务器本身发生错误 500 Internal Server Error服务器端执行请求时发生错误 503 Server Unavailable服务器暂时处于超负载或者正在停机维护,现在无法处理请求]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTTP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[使用 JS + LeanCloud 给网页添加数据库]]></title>
<url>%2F2018%2F06%2F17%2F%E4%BD%BF%E7%94%A8JS%2BLeanCloud%E7%BB%99%E7%BD%91%E9%A1%B5%E6%B7%BB%E5%8A%A0%E6%95%B0%E6%8D%AE%E5%BA%93%2F</url>
<content type="text"><![CDATA[记录给自己网页添加留言功能的过程 使用工具:LeanCloud,一个自带数据库和增删改查(CRUD)功能的后台系统。 1 在JS中引入LeanCloud官方库 在LeanCloud注册并添加应用的步骤: 进入leanCloud官方网站——注册(校验邮箱)——创建应用(不用选什么直接点创建,当然有钱也可以点商用的,随便花,反正我没钱) 安装SKD在页面的右上角处点击 帮助——快速入门选择开发语言或平台——选择 LeanCloud 应用后根据下文的文档根据实际情况安装SKD即可我选择的是CDN方式,直接在HTML中通过 script 标签引入SDK 验证SKD是否安装成功如果安装成功,在chrome的console输入AV,能打印出一串对象,如下图: 初始化SKD在JS中加入如下代码初始化SKD 验证是可以访问 LeanCloud 服务器及添加测试代码验证是可以访问 LeanCloud 服务器及添加测试代码,可看图操作:添加了测试代码后,打开LeanCloud如果看到,Class多了一个项目,项目里内容是Hello World!,即表示测试成功,搬运结束,现在可以去自己的代码中去添加留言区啦。 2 在HTML中添加留言板 用HTML在页面布置留言板12345678910<section class="message"> <h2>留言</h2> <form id="postMessage" class="postMessage" > <span>name:</span> <input type="text" class="name" name="name"> <input type="text" class="content" name="content"> <input type="submit" class="submit" value="留言"> </form> <ol id="messageList" class="messageList"></ol></section> 3 JavaScript思路 思路:将AV 对象封装成 model 对象——通过 controller 操作 model 保存数据、获取数据( 批量获取数据库中的内容并添加到页面中——监听submit事件——保存并提交输入的内容到数据库中——添加功能使新增加的内容自动添加到页面(不用刷新整个页面),同时清除输入框中的内容) 从数据库中获取对象可在LeanCloud中参考API文档: 采用MVC思想写的完整代码如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778!function(){ var model = { //只出现和数据相关的操作 init: function(){ var APP_ID = 'rkk77nVws72ECp28upJUiGec-gzGzoHsz' var APP_KEY = 'LiQjClXYJC8slzGNIYmcB5tO' AV.init({ appId: APP_ID, appKey: APP_KEY }) }, fetch: function(){ var query = new AV.Query('Message') //.find().then(fn1.fn2) 如果成功了调用then的第一个参数,失败了调用第二个参数 return query.find() //返回一个promise对象(后面接的是.then) }, save: function(name,content){ var Message = AV.Object.extend('Message') var message = new Message() return message.save({ name: name, content: content, }) //返回一个promise对象 } } var view = document.querySelector('section.message') var controller = { view: null, model: null, messageList: null, init: function(view,model){ this.view = view this.model = model this.messageList = view.querySelector('#messageList') this.form = view.querySelector('form') this.model.init() this.loadMessage() this.bindEvents() }, loadMessage: function(){ //批量获取数据库中的内容并添加到页面中 this.model.fetch().then( (messages)=> { //.find().then(fn1.fn2) 如果成功了调用then的第一个参数,失败了调用第二个参数 let array = messages.map((item)=> item.attributes) //console.log(messages[0].attributes) array.forEach((item)=> { let li = document.createElement('li') li.innerText = `${item.name}:${item.content}` //为什么不加$会直接显示整个字符串? this.messageList.appendChild(li) }) }, function (error) { console.log('提交失败') // 异常处理 }); }, bindEvents: function(){ //监听submit事件 this.form.addEventListener('submit', (e)=>{ console.log(e) e.preventDefault() //阻止默认事件 this.saveMessage() //一定要使用箭头函数(箭头函数没有this),如果不使用箭头函数,这个this会变成submit的Event }) }, saveMessage: function(){ //保存并提交输入的内容到数据库,并使新增加的内容自动添加到页面,同时清除输入框中的内容 var myForm=this.form let content = myForm.querySelector('input[name=content]').value let name = myForm.querySelector('input[name=name]').value this.model.save(name,content).then( function(object) { console.log('存入成功'); //window.location.reload()用户留言后自动刷新页面,但是会刷新整个页面 //如下方法会自动添加新生成的li,不会刷新页面 let li = document.createElement('li') li.innerText = `${object.attributes.name}:${object.attributes.content}` let messageList = document.querySelector('#messageList') messageList.appendChild(li) //自动提交后自动清空 myForm.querySelector('input[name=content]').value = '' }) } } controller.init(view,model)}.call()]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScipt</tag>
<tag>LeanCloud</tag>
</tags>
</entry>
<entry>
<title><![CDATA[全局变量变局部变量和MVC思想]]></title>
<url>%2F2018%2F06%2F15%2F%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F%E5%8F%98%E4%B8%BA%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E5%92%8CMVC%E6%80%9D%E6%83%B3%2F</url>
<content type="text"><![CDATA[主要介绍了函数中的全局变量如何变成局部变量以及MVC思想 1 函数中的全局变量如何变成局部变量? 全局变量之间会相互骚扰。所以在代码中不要用全局变量。ES6之前只有函数里面有全局变量。 全局变成局部变量怎么变? 把代—放在一个函数如中,再.call()执行一下这个函数?行不行? 不行—样的话函数名也是一个全局变量(全局函数)。 那么—掉函数名把函数变成一个匿名函数?再function(){}.call()立即执行,这样 可以,但是Chrome报错,语法错误。 全局变量变局部变量的方法: 方法一:!function(){}.call( )(前面加+、-、!都可以,这种方法会改变函数的返回值,但是不在乎这个函数的返回值的话加个取反没有关系) 方法二:(function(){}).call( )(用括号把函数括起来。但是不推荐这种做法,因为如果(函数)的前一行被加上一个xxx,很容易被浏览器误解为是在xxx()。) 2 MVC思想 什么是MVC思想MVC 是一种设计模式(或者软件架构),把系统分为三层:Model数据、View视图和Controller控制器。Model 数据管理,包括数据逻辑、数据请求、数据存储等功能。前端 Model 主要负责 AJAX 请求或者 LocalStorage 存储View 负责用户界面,前端 View 主要负责 HTML 渲染。Controller 负责处理 View 的事件,并更新 Model;也负责监听 Model 的变化,并更新 View,Controller 控制其他的所有流程。Model和服务器交互,Model 将得到的数据交给 Controller,Controller 把数据填入 View,并监听 View。用户操作 View,如点击按钮,Controller 就会接受到点击事件,Controller 这时会去调用 Model,Model 会与服务器交互,得到数据后返回给 Controller,Controller 得到数据就去更新 View。 MVC思想的由来MVC是XeroxPARC在八十年代为编程语言Smalltalk发明的一种软件设计模式,至今已被广泛使用。 VC 第一版 123456!function(){ var view = document.querySelector(‘xxx') var controller = function(view){ ..… } controller.call(null,view)}.call() VC 第二版,添加init 12345678910111213!function(){ var view = document.querySelector('xxx') var controller = { view: null, init: function(view){ this.bindEvents(). //this.bindEvents().call(this) }, bindEvents: function(){ ...… }} controller.init(view). //controller.init.call(controller,view)}.call() MVC思想完整版 1234567891011121314151617181920212223242526272829!function(){ //M 模型--用于数据储存,负责和server数据交互 var model = { fetch: function(){ ... }, save: function(){ ... } } //V 视图--负责视图展示 var view = document.querySelector('xxx') //C 控制--负责业务逻辑 var controller = { view: null, model: null, init: function(view,model){ this.view = view this.model = model this.bindEvents() }, bindEvents: function(){ ... } } controller.init(view,model)}.call()]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>DesignModel</tag>
</tags>
</entry>
<entry>
<title><![CDATA[什么是闭包以及闭包的作用]]></title>
<url>%2F2018%2F06%2F10%2F%E4%BB%80%E4%B9%88%E6%98%AF%E9%97%AD%E5%8C%85%E4%BB%A5%E5%8F%8A%E9%97%AD%E5%8C%85%E7%9A%84%E4%BD%9C%E7%94%A8%2F</url>
<content type="text"><![CDATA[一个函数调用了作用域外的一个变量,那么这个函数+这个变量就形成了闭包闭包:立即执行函数使得 匿名函数内的变量 无法被外部访问闭包使得匿名函数可以操作 匿名函数内的变量形成闭包的函数 保存了匿名函数的地址任何地方都可以使用 形成闭包的函数总结:任何地方都可以使用 形成闭包的函数 操作 匿名函数内的变量,但是不能直接访问 匿名函数内的变量]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[本地分支和远程分支的创建与合并]]></title>
<url>%2F2018%2F06%2F04%2F%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF%E5%92%8C%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8E%E5%90%88%E5%B9%B6%2F</url>
<content type="text"><![CDATA[总结整理了操作分支常用的一些指令 1 创建本地分支、提交到远程分支 常用指令 123456789$ git remote -v //可以查看你当前项目的远程git地址 $ git status //查看当前代码状态,改动,所在分支,当前状态有没有代码冲突等$ git branch -a //就是查看远程的所有分支列表了,$ git branch //是查看本地的git分支。绿色代表当前项目所在的分支,红色就是远程分支列表。$ git branch -d test //删除分支$ git checkout test//切换分支$ git pull origin //更新当前指向的分支,当前分支与远程分支已经存在追踪关系$ git diff test//查看分支代码改动$ git merge test //合并test到master上 查看本地分支 12$ git branch * master 查看远程分支(remotes开头的代表是远程分支)123$ git branch -a * master remotes/origin/master 创建本地分支,并切换到分支 12345$ git branch test$ git checkout testS witched to branch 'test'$ git branch master \* test 本地提交到远程 1$ git gui //此时会出现一个窗口根据提示操作就好了 push到远程(第一次无法pull,只能push) 1$ git push origin test:test 从远程pull 12$ git pull origin test:test Already up-to-date. 2 合并分支到master上 假如我们现在在dev分支上,刚开发完项目,执行了命令下列命令 123$ git add $ git commit -m 'test' $ git push -u origin test 然后我们要把dev分支的代码合并到master分支上 该如何?首先切换到master分支上 1$ git checkout master 如果是多人开发的话 需要把远程master上的代码pull下来 1$ git pull origin master 如果是自己一个开发就没有必要了,为了保险期间还是pull然后我们把dev分支的代码合并到master上 1$ git merge test 然后查看状态 12$ git status⚠️ 接着会提示你有n个commit,需要push到远程master上 执行下面命令即可 1$ git push origin master]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[从发请求到AJAX + 什么是同源政策]]></title>
<url>%2F2018%2F06%2F02%2F%E4%BB%8E%E5%8F%91%E8%AF%B7%E6%B1%82%E5%88%B0AJAX-%E4%BB%80%E4%B9%88%E6%98%AF%E5%90%8C%E6%BA%90%E6%94%BF%E7%AD%96%2F</url>
<content type="text"><![CDATA[用form、a、image、link、script等标签都可以发请求,但是各有各的缺陷,这个时候就需要强大的AJAX出场了 1 发请求的各种方法 使用form标签(会在当前页面刷新或者新开一个页面刷新)123<form action="" method=post/get> <input type="submit"></form> 使用a标签(会在当前页面刷新或者新开一个页面刷新)123<a id=x href="">click</a>// 让浏览器帮你自动点击<script>x.click</script> 使用image标签(只能以图片的形式展示) 使用link标签(只能以 CSS、favicon 的形式展示) 123456<script> var link = document.createElement('link') link.rel = 'stylesheet' link.href = ' ' // 必须要放到head中 document.head.appendChild(link) </script> 使用script标签(只能以脚本的形式运行)123456<script> var script = document.createElement('script') script.src = ' ' // 必须要放到head或者body中 document.head.appendChild(script) </script> 等其他几种 2 新的请求方式AJAX AJAX的产生 1231 以上发请求的各种方法都有缺点,那么有什么方式可以实现无论什么请求都行,想用什么形式展示就用什么形式展示?2 IE5在JS中引入activeX对象(API),使 JS 可以直接发起 HTTP 请求(IE从IE6就开始骄傲堕落了。。)3 随后 Mozilla、 Safari、 Opera 也跟进(抄袭)了,取名 XMLHttpRequest,并被纳入 W3C 规范(AJAX奠定了前端的基础) 什么是AJAX? 12345671 AJAX(异步的JavaScript和XML)四个字母的含义(A:异步 J:JavaScript A:and X:XML)2 AJAX需要满足三个条件 使用 XMLHttpRequest 发请求 服务器返回 XML 格式的字符串 JS 解析 XML,并更新局部页面(AJAX默认异步) 3 简单说就是用JS发请求用JS处理响应 4 XML格式的字符串太麻烦了,目前使用JSON 什么是JSON? 1234JavaScript Object Notation, JS 对象简谱,是一种轻量级的数据交换格式(是一门数据格式化语言) 和JS相比数据结构相比没有undefined、function、symbol 和JS相比,JSON的字符串的首尾必须是双引号,格式如{""}(⚠️:JSON中用的都是双引号,没有单引号) 写一个AJAX 123456789101112131415161718192021myButton.addEventListener('onclick',function(){ let request = new XMLHttpRequest() request.open('GET','/xxx') // 想怎么请求就怎么请求 request.send() request.onreadystatechange=function(){ if(request.readyState === 4){ console.log('请求响应完毕了') if(request.status >= 200 && request.status < 300){ // console.log('说明请求成功') // console.log(request.responseText) let string = request.responseText // 把符合JSON语法的字符串,转化成JS对应的值 // JSON.parse是浏览器提供的。document.getElementById也是浏览器提供的(也有很久以前用js自己写过叫json3.js) let object = window.JSON.parse(string) // console.log(object) }else if(request.status >= 400){ // console.log('说明请求失败') } } }}) // 怎么看一句执行用了多长的时间? // console.time(); // var a=1; // console.timeEnd();// readyState的五种状态是(0、1、2、3、4) // 如果状态是4,表示整个请求过程已经完毕 用AJAX设置请求的四个部分12345678910let request = new XMLHttpRequest() // 第一部分:open('请求的方式','请求协议&Host')request.open('GET','/xxx') // 想怎么请求就怎么请求// 第二部分:request.setRequestHeader('设置的类型','设置的内容')request.setRequestHeader('Content-Type','x-www-form-urlencoded') // 第三部分是空格不用设置// 第四部分:request.send('第四部分内容')request.send('第四部分')request.onreadystatechange=function(){} 用AJAX获取响应的四个部分123456789101112131415request.onreadystatechange=function(){ if(request.readyState === 4){ console.log('请求响应完毕了') // 响应的第一部分获取 console.log(request.status) // 200 console.log(request.statusText) // OK if(request.status >= 200 && request.status < 300){ console.log('说明请求成功') // 所有响应的Header获取 console.log(request.getAllResponseHeaders()) // 响应的第二部分获取 console.log(request.getResponseHeader(Content-Type)) // 响应的第三部分是空格 // 响应的第四部分获取 console.log(request.responseText) }} 响应的四个部分是在服务器的node.js里设置的12345678910// 如下为server.js中的代码 // path==='你的路径'if(path === '/'){ // 根据路径造一个字符串 let string = fs.readFileSync('./index.html', 'utf8') // 设置响应的第一部分。statusCode,200/400 response.statusCode = 200 // 设置响应的第二部分 response.setHeader('Content-Type', 'text/html;charset=utf-8') // 设置响应的第四部分 response.write(string) // 然后结束 response.end()} 3 同源政策 同源政策简单说就是:是浏览器安全的基石(只要你不是不是某个页面的JS,就不能向这个页面发起AJAX请求(除了AJAX,其他的请求都可以)) 起源:1995年由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策 目的:是为了保证用户信息的安全,防止恶意的网站窃取数据。(因为AJAX可以读取浏览器响应的内容,如果没有同源政策的限制,就可以随便get、post,则互联网即没有隐私可言) 同源政策的内容:“同源”即”三个相同”,只有 协议+端口+域名 一模一样才允许发 AJAX 请求;如:http:// baidu.com 不可以向 http:// www.baidu.com 发请求。如 http:// baidu.com:80 不可以向 http:// baidu.com:81 发请求)随着互联网的发展,”同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制:1 Cookie、LocalStorage 和 IndexDB 无法读取。2 DOM 无法获得。3 AJAX 请求不能发送。 4 如何规避同源(向另一个协议+端口+域名不一样的网页发起请求) 方法一:用JSONP (不能post) 方法二:用CORS(全称:跨源资源共享 Cross-Origin Resource sharing)在后台加一句允许http:// xxxx这个网站请求,如:response.setHeader('Access-Control-Allow-Origin','http:// xxxx') 方法三:window.postMessage window.postMessage - Web API 接口 | MDN]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>AJAX</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用JSONP实现局部刷新及跨域]]></title>
<url>%2F2018%2F06%2F01%2F%E7%94%A8JSONP%E5%AE%9E%E7%8E%B0%E5%B1%80%E9%83%A8%E5%88%B7%E6%96%B0%E5%8F%8A%E8%B7%A8%E5%9F%9F%2F</url>
<content type="text"><![CDATA[实现HTML页面局部刷新有哪些方法?什么是JSONP?JSONP怎么实现局部刷新和跨域? 1 实现HTML页面局部刷新的方法也可以用iframe方法发get请求,但是目前iframe基本已经被弃用,所以此处就不介绍这个方法。方案一:用图片造 get 请求方案二:用 script 造 get 请求(用script发请求有个问题,不管成功或者失败,都会生成一个script并执行其中的内容。) 2 什么是JSONP?请求方创建 script,src 指向响应方,同时传一个查询参数 ?callbackName=yyy响应方根据查询参数callbackName,构造形如 yyy.call(undefined, ‘你要的数据’) yyy(‘你要的数据’) 这样的响应浏览器接收到响应,就会执行 yyy.call(undefined, ‘你要的数据’)请求方就知道了他要的数据这就是 JSONP 。简单说就是script加callback参数。JSONP的主要方法是通过动态创建script标签并配置的src属性,然后加入页面,触发浏览器加载并执行相应的 JavaScripts 代码,以实现无刷新数据交互的效果。约定:callbackName -> callbackyyy -> 随机数 frank12312312312321325() 3 用JSONP实现局部刷新1234567891011121314151617button.addEventListener('click', (e)=>{ let script = document.createElement('script') let functionName = 'dsfnd'+ parseInt(Math.random()*10000000 ,10) window[functionName] = function(){ // 每次请求之前搞出一个随机的函数 amount.innerText = amount.innerText - 0 - 1 } script.src = '/pay?callback=' + functionName document.body.appendChild(script) script.onload = function(e){ // 状态码是 200~299 则表示成功 e.currentTarget.remove() delete window[functionName] // 请求完了就kill掉这个随机函数 } script.onload = function(e){ // 状态码大于等于 400 则表示失败 e.currentTarget.remove() delete window[functionName] // 请求完了就kill掉这个随机函数 }}) 1234567891011121314//后端代码...if (path === '/pay'){ let amount = fs.readFileSync('./db', 'utf8') amount -= 1 fs.writeFileSync('./db', amount) let callbackName = query.callback response.setHeader('Content-Type', 'application/javascript') response.write(` ${callbackName}.call(undefined, 'success') `) response.end()}... 4 用JSONP实现跨域请求方:aaa.com 的前端程序员(浏览器)响应方:bbb.com 的后端程序员(服务器)假设有aaa.com、bbb.com 两个网站,aaa.com的前端想要访问bbb.com 的后端,aaa.com的前端可在请求的“/pay”前面加上bbb.com 的域名。通过跨域SRG。后端不需要太了解前端的代码,如果太了解,就叫前端后端耦合,需要解耦。解耦的方法:后段调用前端提供的一个函数。 5 JSONP 为什么不支持 POST?因为JSONP是通过动态创建Script实现的,动态创建Script的时候只能用GET,不能用POST。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JSONP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[从零开始学习SVG]]></title>
<url>%2F2018%2F06%2F01%2F%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0SVG%2F</url>
<content type="text"><![CDATA[SVG是一种用来描述二维矢量图形的标记语言 1 什么是SVG? MDN中的定义是:SVG即可缩放矢量图形(Scalable Vector Graphics,SVG),是一种用来描述二维矢量图形的 XML 标记语言。 简单地说,SVG 面向图形,HTML 面向文本。 SVG 与 Flash 类似,都是用于二维矢量图形,二者的区别在于,SVG 是一个 W3C 标准 ,基于 XML,是开放的,而 Flash 是封闭的基于二进制格式的。因为都是 W3C 标准,SVG 与其他的 W3C 标准,比如 CSS 、 DOM 和 SMIL 等能够协同工作。 2 SVG的坐标系统对于所有元素,SVG使用的坐标系统或者说网格系统,和 Canvas 用的差不多(所有计算机绘图都差不多)。这种坐标系统是:以页面的左上角为(0,0)坐标点,坐标以像素为单位,x轴正方向是向右,y轴正方向是向下。注意,这和你小时候所教的绘图方式是相反的。但是在HTML文档中,元素都是用这种方式定位的。SVG 3 画图形 画矩形(rect) 12345678910<rect x="60" y="10" rx="10" ry="10" width="30" height="30"/>/*x 矩形左上角的x位置y 矩形左上角的y位置width 矩形的宽度height 矩形的高度rx 圆角的x方位的半径 ry 圆角的y方位的半径*/ 画圆形(circle) 123456<circle cx="25" cy="75" r="20"/>/*r 圆的半径cx 圆心的x位置cy 圆心的y位置*/ 画椭圆(ellipse) 1234567<ellipse cx="75" cy="75" rx="20" ry="5"/>/*rx 椭圆的x半径ry 椭圆的y半径cx 椭圆中心的x位置cy 椭圆中心的y位置*/ 画直线(line) 1234567<line x1=“10” x2=“50” y1=“110” y2=“150”/>/*x1 起点的x位置y1 起点的y位置x2 终点的x位置y2 终点的y位置*/ 画折线(polyline) 12345<polyline points="60 110, 65 120, 70 115, 75 130, 80 125, 85 140, 90 135, 95 150, 100 145"/>/*points点集数列。每个数字用空白、逗号、终止命令符或者换行符分隔开。每个点必须包含2个数字,一个是x坐标,一个是y坐标。所以点列表 (0,0), (1,1) 和(2,2)可以写成这样:“0 0, 1 1, 2 2”。*/ 画多边形(polygon) 12345<polygon points="50 160, 55 180, 70 180, 60 190, 65 205, 50 195, 35 205, 40 190, 30 180, 45 180"/>/*points点集数列。每个数字用空白符、逗号、终止命令或者换行符分隔开。每个点必须包含2个数字,一个是x坐标,一个是y坐标。所以点列表 (0,0), (1,1) 和(2,2)可以写成这样:“0 0, 1 1, 2 2”。路径绘制完后闭合图形,所以最终的直线将从位置(2,2)连接到位置(0,0)。*/ 画路径(path) 1<path d="M 20 230 Q 40 205, 50 230 T 90230"/>]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>SVG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[onclick与addEventListener的区别]]></title>
<url>%2F2018%2F06%2F01%2Fonclick%E4%B8%8EaddEventListener%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[通过onclick与addEventListener及两个实例子深度解析DOM的冒泡及捕获事件 1 实例1—点击别处关闭浮层代码效果演示: 原生JS 、 jQuery 2 实例2—点击后颜色一层一个层出现的漂亮的彩虹圈代码效果: 彩虹圈 3 onclick与addEventListener的区别实例1使用的原生JS,为什么使用addEventListener(),而不使用onclick() —onclick()只能添加一个事件,多个事件时只会输出最后一个,而实例1中存在多个事件,不能用onclick()onclick与addEventListener实际上可分为:Inline events与Event Listeners Event Listeners (“addEventListener” and “IE’s attachEvent”)两者相同点:都是时间监听器。两者不同点: addEventListener:很多浏览器支持addEventListener(IE9、IE10、IE11、chrome、firefox、opera、safari支持),使用方式如下: 123456//addEventListener接受三个参数,最后一个参数默认是false。(false表示事件处理将在冒泡阶段执行,true表示事件处理将在捕获阶段执行)//addEventListener(type,listener,options) var target = document.getElementById("test");target.addEventListener('click',function test(){ console.log("Hi");},false) attachEvent:IE中提供的类似addEventListener的事件监听器,使用方式如下: 123456//qqqvar target = document.getElementById("test");target.attachEvent('onclick',test);function test(){ console.log("Hi");} 理论上,Event Listeners (addEventListener and IE’s attachEvent)可以无限增加事件监听给某个一元素。实际应用的约束就是客户端内存的限制,这一点因每个浏览器而异 123456789var target = document.getElementById("test");target.addEventListener('click',function test(){ console.log("Hi");},false)target.addEventListener('click',function test(){ console.log("Hello");},false)//Hi//Hello Inline events (“HTML onclick=“” property” and “element.onclick”)使用方式: onclick=“” 1<a id="test" href="#" onclick="function()">clickMe</a> element.onclick 123456<a id="test" href="#">clickMe</a> var target = document.getElementById('test')target.onclick = function(){ console.log('Hi');} Inline events只能添加一个事件,如果同时有多个,只会输出最后一个的结果 12345678var target = document.getElementById('tttt')target.onclick = function(){ console.log('Hi');}target.onclick = function(){ console.log('Hello');}//Hello Inline events与Event Listeners的区别Event Listeners可以添加无数个(理论上)事件,Inline events只能添加1个事件,且下面的会覆盖上面的。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTML/DOM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[使用http-server开启一个本地服务器]]></title>
<url>%2F2018%2F05%2F12%2F%E4%BD%BF%E7%94%A8http-server%E5%BC%80%E5%90%AF%E4%B8%80%E4%B8%AA%E6%9C%AC%E5%9C%B0%E6%9C%8D%E5%8A%A1%E5%99%A8%2F</url>
<content type="text"><![CDATA[在写前端页面中,经常会在浏览器运行HTML页面,从本地文件夹中直接打开的一般都是file协议,当代码中存在http或https的链接时,HTML页面就无法正常打开,为了解决这种情况,需要在在本地开启一个本地的服务器本文是利用node.js中的http-server,开启本地服务,步骤如下: 1 下载node.js官网地址: https://nodejs.org下载完成后在命令行输入命令$ node -v以及$ npm -v检查版本,确认是否安装成功。 2 下载http-server在终端输入:$ npm install http-server -g 3 开启 http-server服务终端进入目标文件夹,然后在终端输入:123456$ http-server -c-1 (⚠️只输入http-server的话,更新了代码后,页面不会同步更新)Starting up http-server, serving ./Available on: http://127.0.0.1:8080 http://192.168.8.196:8080Hit CTRL-C to stop the server 4 关闭 http-server服务按快捷键CTRL-C终端显示^Chttp-server stopped.即关闭服务成功。]]></content>
<categories>
<category>备忘</category>
</categories>
</entry>
<entry>
<title><![CDATA[使用JS方法使页面滚动到指定元素+优化]]></title>
<url>%2F2018%2F05%2F10%2F%E4%BD%BF%E7%94%A8JS%E6%96%B9%E6%B3%95%E4%BD%BF%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8%E5%88%B0%E6%8C%87%E5%AE%9A%E5%85%83%E7%B4%A0-%E4%BC%98%E5%8C%96%2F</url>
<content type="text"><![CDATA[当页面最上部有顶部菜单是,使用锚点跳转的方法很容易挡住想要呈现的内容(如字被挡了一半),为避免出现这样的问题,故滚动到指定元素使用用JS的方法来实现 1 初版(第一版)123456789101112131415161718//1 获取所有的a标签let aTags=document.querySelectorAll("nav.menu ul li a") //console.log(aTags)//2 遍历a标签并点击标签滚动到指定元素位置for(let i=0;i<=aTags.length;i++){ aTags[i].onclick=function(x){ x.preventDefault(); //阻止a标签默认的跳转 //console.log(x.currentTarget); let a=x.currentTarget; let href=a.getAttribute("href"); //找到href中的内容,如果href中时一个锚点则返回#siteSkill //console.log(href); let element=document.querySelector(href); //找到内容中的锚点对应ID的标签,如对应的锚点名为#siteSkill,则返回<section class="skills" id="siteWorks">…</section> //console.log(element); let top=element.offsetTop; //获取元素到页面最顶点的高度(不会随着页面滚动变化的高度) //console.log(top); window.scrollTo(0,top-80); }} 这样能准确的达到想要的地方并且也不会内容也不会被挡住,但是,也存在一些缺点,比如跳转太生硬,中间没有过渡,影响用户体验。 2 优化(第二版)12345678910111213141516171819202122232425262728//1 获取所有的a标签let aTags=document.querySelectorAll("nav.menu ul li a") //console.log(aTags)//2 遍历a标签并点击标签跳到指定元素位置for(let i=0;i<=aTags.length;i++){ aTags[i].onclick=function(x){ x.preventDefault(); //阻止a标签默认的跳 let a=x.currentTarget; let href=a.getAttribute("href"); //找到href中的内容,如果href中时一个锚点则返回#siteSkill let element=document.querySelector(href); //找到内容中的锚点对应ID的标签,如对应的锚点名为#siteSkill,则返回<section class="skills" id="siteWorks">…</section> let top=element.offsetTop; let n=25; //动的次数 let t=500/n; //多久动一次 let currentTop=window.scrollY; //所在的位置 let targetTop=top-80; //目标位置 var s=(targetTop-currentTop)/n; //每次动的距离 let i=0; let id=setInterval(()=>{ if(i===n){ window.clearInterval(id); return; } //当i=n时停止动画 i=i+1 window.scrollTo(0,currentTop+s*i) },t) }} 优化后有跳转动画,但是依然还有缺点,比如:定义的是时间一致,所以跳转到距离TOP不同位置的地方速度不一致。看起来依然生硬不自然 3 继续优化(第三版 引入tween.js库)123456789101112131415161718192021222324252627282930313233343536//1 引入tween.js库<script src='https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.min.js'></script> <script> //2 获取所有的a标签 let aTags=document.querySelectorAll("nav.menu ul li a"); function animate(time){ requestAnimationFrame(animate); TWEEN.update(time);}requestAnimationFrame(animate);//3 遍历a标签并在点击标签时滚动到指定元素的位置for(let i=0;i<=aTags.length;i++){ aTags[i].onclick=function(x){ x.preventDefault(); //阻止a标签默认的跳 let a=x.currentTarget; let href=a.getAttribute("href"); //找到href中的内容,如果href中时一个锚点则返回#siteSkill let element=document.querySelector(href); //找到内容中的锚点对应ID的标签,如对应的锚点名为#siteSkill,则返回<section class="skills" id="siteWorks">…</section> let top=element.offsetTop; let currentTop=window.scrollY; //所在的位置 let targetTop=top-80; //目标位 let s=targetTop-currentTop; //所在到目标的高度差 let t=Math.abs((s/100)*200) //Math.abs方法保证时间为正值不为负数。ps:Math的首字母需要大写!!! var coords={y:currentTop}; //y为所在位置 if(t>=500){t=500} //如果时间最大为500,不超过500 var tween=new TWEEN.Tween(coords) .to({y:targetTop},t) //y为到达目标位置,时间 .easing(TWEEN.Easing.Quadratic.In) .onUpdate(function(){ window.scroll(0,coords.y) }) .start(); }}</script>]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[实现两个jQuery的API(addClass、text)]]></title>
<url>%2F2018%2F05%2F10%2F%E5%AE%9E%E7%8E%B0%E4%B8%A4%E4%B8%AAjQuery%E7%9A%84API%EF%BC%88addClass%E3%80%81text%EF%BC%89%2F</url>
<content type="text"><![CDATA[用原生JS实现jQuery的addClass和text两个API 目的Quick Start给所有的div添加一个叫“red”的class,为方便看到代码的效果,设置如下css,在设置“red”成功时,文本会变红123.red{ color:red;} 将所有的div中的textContent变为“Hi”,HTML代码如下:1234567<body> <div class="item1">选项1</div> <div class="item2">选项2</div> <div class="item3">选项3</div> <div class="item4">选项4</div> <div class="item5">选项5</div></body> 思路完整代码及思路如下,效果 点击这里]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[利用@keyframe及animation做一个页面Loading时的小动画]]></title>
<url>%2F2018%2F05%2F08%2F%E5%88%A9%E7%94%A8-keyframe%E5%8F%8Aanimation%E5%81%9A%E4%B8%80%E4%B8%AA%E9%A1%B5%E9%9D%A2Loading%E6%97%B6%E7%9A%84%E5%B0%8F%E5%8A%A8%E7%94%BB%2F</url>
<content type="text"><![CDATA[利用@keyframe规则和animation常用属性做一个页面Loading时的小动画 1 @keyframe规则简介 @keyframes定义关键帧,即动画每一帧执行什么。要使用关键帧, 先创建一个带名称的@keyframes规则,以便后续使用 animation-name 这个属性来调用指定的@keyframes. 每个@keyframes 规则包含多个关键帧,也就是一段样式块语句,每个关键帧有一个百分比值作为名称,代表在动画进行中,在哪个阶段触发这个帧所包含的样式。关键帧的编写顺序没有要求,最后只会根据百分比按由小到大的顺序触发。 语法12345@keyframes name { from { } to { }}<!-- 注意⚠️:@keyframes 不能在内联样式中使用 --> 2 animation常用属性简介 animation定义动画每一帧如何执行。该属性允许配置动画时间、时长以及其他动画细节,但该属性不能配置动画的实际表现,动画的实际表现是由 @keyframes 规则实现。 animation的属性 animation-delay:设置延时,即从元素加载完成之后到动画序列开始执行的这段时间,单位一般为秒(s)或毫秒(ms),若为负值表示跳过前几秒执行。 animation-direction:设置动画在每次运行完后是反向运行还是重新回到开始位置重复运行。 * normal:默认值,动画按正常播放; * reverse:动画反向播放; * alternate:动画在奇数次正向播放,在偶数次反向播放; * alternate-reverse:动画在奇数次反向播放,在偶数次正向播放; * initial:设置该属性为它的默认值; * inherit:从父元素继承该属性。 animation-duration:设置动画一个周期的时长。 animation-iteration-count:设置动画重复次数, 可以指定infinite无限次重复动画 animation-name:指定由@keyframes描述的关键帧名称。 animation-play-state:允许暂停和恢复动画。 * paused:指定动画暂停; * running:指定动画运行; animation-timing-function:设置动画速度, 即通过建立加速度曲线,设置动画在关键帧之间是如何变化。 animation-fill-mode:指定动画执行前后如何为目标元素应用样式。 3 实例:一个页面Loading时的小动画点击查看动画效果点击]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JavaScript数据类型判断]]></title>
<url>%2F2018%2F04%2F28%2FJavaScript%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%88%A4%E6%96%AD%2F</url>
<content type="text"><![CDATA[Typeof()、instanceof运算符、Object.prototype.toString.call()、constructor属性都可以判断数据类型 1 Typeof()判断基本数据类型(基本类型number、string、boolean、undefined,除了null。)⚠️不能区分对象、数组、null等123456789101112//typeof()输出有五种数据类型 number string boolean undefined object functiontypeof("") //"string"typeof(1) //"number"typeof(true) //"boolean"typeof(undefined) //"undefined"typeof({}) //"object"typeof([]) //"object" array返回对象typeof(function(){}) //"function"typeof(null) //"object" null返回对象//但是怎么区分 对象、数组以及null 呢? 2 instanceof运算符判断引用类型(引用类型,即对象类型。创建对象后可以调用这个对象下的方法有Object类型、Array类型、Date类型、RegExp类型、Function类型,包装类型(Boolean、Number、String)等。)123456789//instanceof对引用类型进行判断{} instanceof Object; //true[] instanceof Array; //truenew Date() instanceof Date; //truefunction(){} instanceof Function; //true//instanceof无法对原始类型进行判断"string" instanceof String; //false(111) instanceof Number; //false 3 Object.prototype.toString.call()能准确的判断基本类型和引用类型123456789Object.prototype.toString.call('abc') //"[object String]"Object.prototype.toString.call(123) //"[object Number]"Object.prototype.toString.call(true) //"[object Boolean]"Object.prototype.toString.call(undefined) //"[object Undefined]"Object.prototype.toString.call(null) //"[object Null]"Object.prototype.toString.call({}) //"[object Object]"Object.prototype.toString.call([]) //"[object Array]"Object.prototype.toString.call(function(){}) //"[object Function]" 4 constructor属性Constructor属性始终指向创建当前对象的构造函数12345"string".constructor == String //truetrue.constructor == Boolean //true(123).constructor == Number //true{}.constructor == Object //true[].constructor == Array //true 一个常用的函数123function isArray(arr){ return typeof arr == “object” && arr.constructor == Array;}]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JavaScript数据类型转换]]></title>
<url>%2F2018%2F04%2F27%2FJavaScript%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2%2F</url>
<content type="text"><![CDATA[任意数据类型转字符串、数字、布尔 1 任意转字符串String(thing) (thing:任何可以被转换成字符串的值12345String(1) //"1"String(true) //"true"String(null) //"null"String(undefined) //"undefined"String({}) //"[object Object]" 注意⚠️:当字符串中的数字为其他进制时,会自动转化为十进制,再把十进制转化为字符串,如:1234String(0b1100) //"12" 二进制转化为十进制String(01100) //"576" 八进制转化为十进制String(0o1100) //"576" 八进制转化为十进制String(0x1100) //"4352" 十六进制转化为十进制 thing.toString()12345671.toString() //Uncaught SyntaxError: Invalid or unexpected token1..toString() //"1" (1).toString() //"1" true.toString() //"true"null.toString() //VM285:1 Uncaught TypeError: Cannot read property 'toString' of null at <anonymous>:1:6undefined.toString() //VM283:1 Uncaught TypeError: Cannot read property 'toString' of undefined at <anonymous>:1:11{}.toString() //"[object Object]" thing + “”12345671 + "" //"1"true + "" //"true"null + "" //"null"undefined + "" //"undefined"{} + "" //0var o = {} o + "" //"[object Object]" 2 任意转数字Number(value)12345678910Number(true) //1 布尔转为数字Number(false) //0 布尔转为数字Number(null) //0 null转为数字Number(undefined) //NaN undefined转为数字,结果为NaN Number("123") //123 字符串转为数字Number(" ") //0 有空格的空字符串为0Number("") //0 为空格的空字符串为0Number("123a") //NaN number转数字的字符串中不能有字母,parseFloat以及parseInt的中间可以有字母,但是开头不能有Number("true") //NaNNumber("false") //NaN parseInt(string, radix) MDN 123456789101112//⚠️string必须是一个字符串//⚠️如果不是字符串而是一串数字(注意不能是数字+字母的格式),系统会自动把数字转为字符串,parseInt(01100)相当于是parseInt(String(01100)),由于01100是0开头,是一个八进制,String(01100)会把进制度转化为十进制再转为字符串,即相当于是parseInt("576")parseInt("01100", 10) //1100 parseInt(01100) //576 parseInt("01100", 8) //576 8的意思是:我字符串中的值是八进制的,请把它转化为十进制//⚠️但是0x开头(即十六进制)的除外parseInt("0b1100") //0 二进制返回0,到字母b处即无法识别数字parseInt("0o1100") //0 八进制返回0,到字母o处即无法识别数字parseInt("0x1100") //4352 括号里的是字符串。十六进制返回对应的十进制parseInt(0x1100) //4352 括号里的不是字符串。十六进制返回对应的十进制 * string:必需。要被解析的字符串。 * radix:可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间,如果省略该参数或其值为 0,则数字将以 10 为基础来解析。**如果它以 “0x” 或 “0X” 开头,将以 16 为基数**,如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。 * 注意⚠️:radix参数为n 将会把第一个参数看作是一个数的n进制表示,而返回的值则是十进制的 parseFloat(string) MDN123456789101112parseFloat("1.11") //1.11 字符串中是数字(0-9)以及小数点parseFloat(".1.11") //0.1 如果首字符是.则会自动shiparseFloat("0.0111e2") //1.11 字符串中是科学计数法(e/E)parseFloat("+1.11") //1.11 字符串中有+parseFloat("-1.11") //-1.11 字符串中有-parseFloat("1.11more") //1.11 字符串中如果有除 小数点、+/-、数字、e/E 的字符,它以及之后的字符都会被忽略parseFloat("more1.11") //NaN 如果以字母开头直接NaNparseFloat(" 1.11") //1.11 字符串开头的空格会自动忽略//注意⚠️parseFloat(".1.11") //0.1 如果字符串有两个点,第二点之后的内容会被忽略掉parseFloat("..1.11") //NaN 开头多个点会NaN * string:需要被解析成浮点数的字符串 * parseFloat是个全局函数,不属于任何对象 * parseFloat将它的字符串参数解析成为浮点数并返回,如果在解析过程中遇到了**正负号(+或-)、数字(0-9)、小数点、或者科学记数法中的指数(e或E)以外的字符**,则它**会忽略该字符以及之后的所有字符**,返回当前已经解析到的浮点数,同时**参数字符串首位的空白符会被忽略** * 可以通过调用isNaN函数来判断parseFloat的返回结果是否是NaN,如果让NaN作为了任意数学运算的操作数,则运算结果必定也是NaN * parseFloat 也可转换和返回Infinity值. 可以使用isFinite 函数来判断结果是否是一个有限的数值 (非Infinity, -Infinity, 或 NaN) string- 0 或 string*1 或string/1 1234567"123"-0//123 相减"123"*1//123 相乘"123"/1//123 相除//注意⚠️"123a"-0//NaN 字符串中不能有字母 +string 或 -string12+"123"//123 正-"123"//-123 负 3 任意转布尔Boolean(value) (value:可选,是用来初始化 Boolean 对象的值。)1234567891011//Boolean()值为false的情况:参数值为 0、-0、null、NaN、undefined、空字符串(""),或者传入的参数为 DOM 对象的 document.all 时Boolean(""); //false Boolean(0); //falseBoolean(-0); //falseBoolean(NaN); //falseBoolean(null); //false Boolean(undefined); //false //Boolean()值为True的情况:除以上提到的几种情况,任何其他的值,包括值为 "false" 的字符串和任何对象,都会创建一个值为 true 的 Boolean 对象。//⚠️空字符串中如果有空格,返回的是trueBoolean(" "); //true !!value123456!!"" //false!!0 //false!!-0 //false!!NaN //false!!null //false!!undefined //false]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JavaScript数据类型介绍]]></title>
<url>%2F2018%2F04%2F26%2FJavaScript%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[最新的 ECMAScript 标准定义了JS的 7 种数据类型,其中包括:6 种基本类型:Boolean、Null、Undefined、Number、String、Symbol (ECMAScript 6 新定义);1个引用类型: Object(包含狭义的对象,Array,function)。两种类型的值传递方式:基本类型是值传递,引用类型是引用传递。12345678910111213// 第一题 引用传递function test(m) { m.k = 5 }var m = { k: 30}test(m)console.log(m.k) // 5// 第二题 值传递function test(m) { m = 5 }var m = 30test(m)console.log(m) // 30 1 Boolean Bealean类型的值有两个:true、false 所有其他数据类型都有对应的Boolean值,使用Boolean(value)方法可以强制转换任意值为boolean类型1console.log(Boolean("Hello")); //true 2 Null Null类型的值只有一个:null Typeof(null)时返回“object”:这是历史原因造成的,但是可以理解成:unll表示一个空对象(Object)的引用1typeof(null); // “object” 3 Undefined undefined类型的值只有一个:undefined 只进行了声明而未初始化的变量,其值都是 12undefined var m;console.log(m); //undefined undefined值派生自null值,两者都是表示“没有值”。两者相等,但是由于数据类型不一样,两者不全等(==是相等操作符会对数据类型进行转化,===是全等操作符不会转化数据类型) 12console.log(undefined == null); //true console.log(undefined === null); //false 如何区分undefined和null:表示一个还没赋值的对象用null;表示一个还没赋值的字符串、数字、布尔、symbol时用undefined 4 Number Number包括:整数和小数(如:1/1.1)、科学计数法(如:1.11e2)、二进制(如:0b11)、八进制(如:011或0o11)、十六进制(如:0x11) 保存浮点数所需的内存空间是整数值的2倍 1* 浮点数值相加结果会不准确 console.log(0.1+0.2); //0.30000000000000004 NaN是一个特殊的Number值;它的存在是为了避免程序直接报错;NaN的任何操作都会返回NaN;NaN与任何值都不相等,包括它自身 1console.log(NaN === NaN); //false; 5 String 字符串String类型是由引号括起来的一组由16位Unicode字符组成的字符序列。 用单引号(’ ‘)或双引号(” “)皆可,但是必须双引号配双引号,单引号配单引号 任何字符串的长度都是可以通过length属性来取得 var a=“nihao”; 12console.log(a.length);//5 ECMAScript中字符串是不可变,如要改变该变量保存的字符串,首先要销毁原来的字符串,再用另一个包含新值的字符串填充该变量 6 Symbol symbol是基本类型,实现唯一标识 通过调用symbol(name)创建symbol name 我们创建一个字段,仅为知道对应symbol的人能访问,使用symbol很有用 symbol不会出现在for..in结果中 使用symbol(name)创建的symbol,总是不同,即使name相同。如果希望相同名称的symbol相等,则使用全局注册 symbol.for(name)返回给定名称的全局symbol,多次调用返回相同symbol Javascript有系统symbol,通过Symbol.*访问。我们能使用他们去修改一些内置行为 7 Object 对象由 { } 分隔,在 { } 内部,对象的属性以名称和值对的形式 (name : value) 来定义,属性由逗号分隔 123456789var cars={"car1" : "Volvo","car2": "Saab",}; //或者var cars={car1 : "Volvo",car2: "Saab",}; 寻找对象中的值有两种方式: 12car1name=cars.car1;car1name =cars["car1"]; 数组(Array)和函数(Function)是高级的对象 注意⚠️:基本类型和引用类型的区别: 基本类型:访问是按值访问值不可变,基本类型的比较是值的比较,数据是存放在栈内存中的 引用类型:拥有属性和方法且值是可变的,引用类型的比较是引用的比较,数据是存放在堆内存中的]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[使用终端改变MAC默认截图存放地址]]></title>
<url>%2F2018%2F04%2F21%2F%E4%BD%BF%E7%94%A8%E7%BB%88%E7%AB%AF%E6%94%B9%E5%8F%98MAC%E9%BB%98%E8%AE%A4%E6%88%AA%E5%9B%BE%E5%AD%98%E6%94%BE%E5%9C%B0%E5%9D%80%2F</url>
<content type="text"><![CDATA[使用终端改变MAC默认截图存放地址的过程主要分为两步: 第一步:输入如下命令,回车1defaults write com.apple.screencapture location 要存放到的位置的绝对路径 第二步:输入如下命令,应用刚才的操作1killall SystemUIServer 操作代码如下图:img]]></content>
<categories>
<category>备忘</category>
</categories>
</entry>
<entry>
<title><![CDATA[让.bashrc文件在终端自动生效]]></title>
<url>%2F2018%2F04%2F19%2F%E8%AE%A9bashrc%E6%96%87%E4%BB%B6%E5%9C%A8%E7%BB%88%E7%AB%AF%E8%87%AA%E5%8A%A8%E7%94%9F%E6%95%88%2F</url>
<content type="text"><![CDATA[修改了.bashrc文件,想在打开终端时默认路径变成桌面路径。代码如下12cd ~/desktopexport PATH="/Users/nola/local:$PATH" 但是每次通过ssh打开终端都需要重新source ~/.bashrc一下,十分麻烦。于是今天终于找到一个办法,就是在.bash_profile文件里重新引用一次.bashrc,添加的代码如下:123if test -f .bashrc ; thensource .bashrcfi]]></content>
<categories>
<category>备忘</category>
</categories>
<tags>
<tag>Bash</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HTTP的请求及响应]]></title>
<url>%2F2018%2F04%2F17%2FHTTP%E7%9A%84%E8%AF%B7%E6%B1%82%E5%8F%8A%E5%93%8D%E5%BA%94%2F</url>
<content type="text"><![CDATA[HTTP是什么?HTTP 请求包括哪些部分?HTTP 响应包括哪些部分?如何使用 curl 命令? 1 HTTP是什么?HTTP全称:HyperText Transfer Protocol,即超文本传输协议。HTTP作用:指导浏览器和服务器之间进行沟通。 2 HTTP 请求包括哪些部分?HTTP请求主要包括四部分(第四部分可以为空),主要格式如下:1234567891 动词 路径 协议/版本2 Key1: value12 Key2: value22 Key3: value32 Content-Type: application/x-www-form-urlencoded2 Host: www.baidu.com2 User-Agent: curl/7.54.03 4 要上传的数据 3 HTTP 响应包括哪些部分?HTTP响应同样包括四部分,主要格式如下:12345671 协议/版本号 状态码 状态解释2 Key1: value12 Key2: value22 Content-Length: 179312 Content-Type: text/html34 要下载的内容 4 如何用Chrome开发者工具查看 HTTP 请求及响应的内容? 首先进入chrome浏览器,command+option+i打开开发者页面,选择Network,选择一个请求,查看Hearders 查看请求头部信息:打开request hearders即可看到请求头 查看响应头部信息:打开response hearders即可看到响应头 查看响应的内容,点击Hearders旁边的Preview即可 5 如何使用 curl 命令? 什么是curl:curl是Linux下一个很强大的http命令行工具。 curl的基本用途:创造一个请求,并得到响应:1234567891011121314151617181920$ curl -s -v -H "Nola: xxx" \-\- "https://www.baidu.com" 请求内容:GET / HTTP/1.1 Host: www.baidu.comUser-Agent: curl/7.54.0 Accept: */* Nola: xxx$ curl -X POST -s -v -H "Nola: xxx" -- "https://www.baidu.com"请求内容:POST / HTTP/1.1Host: www.baidu.comUser-Agent: curl/7.54.0Accept: */* Nola: xxx$ curl -X POST -d "1234567890" -s -v -H "Nola: xxx" \-\- "https://www.baidu.com" 请求内容:POST / HTTP/1.1 Host: www.baidu.comUser-Agent: curl/7.54.0 Accept: */* Nola: xxxContent-Length: 10Content-Type: application/x-www-form-urlencoded 1234567890]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTTP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[从点击url到浏览器显示页面,这个过程中发生了什么?]]></title>
<url>%2F2018%2F04%2F13%2F%E4%BB%8E%E7%82%B9%E5%87%BBurl%E5%88%B0%E6%B5%8F%E8%A7%88%E5%99%A8%E6%98%BE%E7%A4%BA%E9%A1%B5%E9%9D%A2%EF%BC%8C%E8%BF%99%E4%B8%AA%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F%2F</url>
<content type="text"><![CDATA[从点击url到浏览器显示页面这个过程可以分为六步 浏览器通过域名找出其IP地址(DNS解析)客户端先检查本地是否有对应的IP地址,若找到则返回响应的IP地址。若没找到则请求上级DNS服务器,直至找到或到根节点。(浏览器缓存→系统缓存→路由器缓存→ISP DNS缓存→从根域名服务器递归搜索) 注意⚠️: URL和域名的区别:域名:需要注册和购买,域名经过解析为ip地址。。。就是说用ip地址和域名都可以访问同一资源。url:简单说网址=ip或域名 + 端口号 + 资源位置 + 参数 + 锚点 IP 地址与域名的关系:多个提供相同服务的服务器 IP 可设置为同一个域名,但同一时刻一个域名只能解析出一个 IP地址。一个 IP 地址可以绑定多个域名。 若修改hosts文件,可自己指定域名的IP,绕过DNS。 浏览器和服务器建立连接(TCP/TP三次握手) 客户端发送SYN包到服务器,等待服务器确认接收。(浏览器问服务器:我可以连接你吗?) 服务器确认接收SYN包并确认客户的SYN,并发送回来一个SYN+ACK的包给客户端。(服务器:好,连吧) 客户端确认接收服务器的SYN+ACK包,并向服务器发送确认包ACK,二者相互建立联系完成TCP三次握手后,就可以开始传输数据了。(浏览器:嗯,那我连接了) 浏览器向服务器发送HTTP请求建立连接成功后,浏览器给web服务器发送一个HTTP请求。 服务器接受到请求并返回HTTP响应服务器接到请求后,会根据 HTTP 请求中的内容来决定如何获取相应的文件,并将文件发送给浏览器 浏览器解析渲染页面浏览器根据响应先解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。 客户端和服务端发送四次数据包断开连接(四次挥手) 客户端主动关闭连接,发送FIN报文给服务器,然后进入FIN_WAIT_1状态。(浏览器问服务器:不早了,我该走了) 服务器收到FIN报文,回应一个ACK报文,进入CLOSED_WAIT状态;客户端收到FIN报文,进入FIN_WAIT_2状态。(服务器:知道了) 服务器向客户端发送FIN报文,进入LAST_ACK状态。(服务器:我也该走了) 客户端收到FIN报文后,向服务器发送ACK报文,进入TIME_WAIT状态,等待2MLS(它是任何报文在网络丢弃前在网络内的最长时间)后客户端就自动关闭。服务器在接收数据后经过判断无误后,服务器进入关闭状态。(浏览器回复:嗯,好的,然后等待2MLS。同时服务器那边收到浏览器的回复后断开连接)]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>HTTP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[写一个简单的脚本,并在脚本生成的的文件中添加内容]]></title>
<url>%2F2018%2F04%2F12%2F%E5%86%99%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E8%84%9A%E6%9C%AC%EF%BC%8C%E5%B9%B6%E5%9C%A8%E8%84%9A%E6%9C%AC%E7%94%9F%E6%88%90%E7%9A%84%E7%9A%84%E6%96%87%E4%BB%B6%E4%B8%AD%E6%B7%BB%E5%8A%A0%E5%86%85%E5%AE%B9%2F</url>
<content type="text"><![CDATA[如何写一个简单的脚本?如何在脚本生成的文件中添加内容? 1 如何写一个简单的脚本?写一个输入脚本名称即可自动生成一个任意名字的文件夹,并且文件夹中包括可以包括任意文件的,简单的脚本,只需要简单的四步:1234567891011121314151617第一步:新建一个脚本文件,后缀随意,一般来说脚本的后缀是 .sh,我这里设置为.txt是为了方便编辑。新建一个脚本文件可在终端输入: touch demo.txt第二步:输入在终端输入"open demo.txt"进入 demo.txt文件进行编辑(生成一个任意名称的文件夹,文件夹中包括一个"index.html"文件和一个"style.css"文件),编辑内容如下: mkdir $1 cd $1mkdir css js touch index.html style.css exit注意⚠️:$1 表示你传的第一个参数。第三步:给 demo.sh 添加执行权限。在终端中输入: chmod +x demo.txt 此时在终端输入如下代码即可运行: `sh demo.txt`第四步:其实第三步就已经建立了一个脚本了,第四的作用是,如果你想修改这个脚本文件的后缀即可使用。在终端输入: mv demo.txt demo(你想改成任何后缀都可以) 2 如何在脚本生成的文件中添加内容? 在脚本生成的文件中添加内容很简单,只需要打开脚本demo.txt(如果修改了脚本后缀可用修改后的后缀)。 如:在”index.html”中添加内容1"<!DOCTYPE><title>Hello</title><h1>Hi</h1>" 打开脚本demo.txt在内容里添加如下一行内容即可:1echo "<!DOCTYPE><title>Hello</title><h1>Hi</h1>" >\> index.html]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Bash</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用CSS伪类制作一个不断旋转的八卦图]]></title>
<url>%2F2018%2F04%2F10%2F%E7%94%A8CSS%E4%BC%AA%E7%B1%BB%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E4%B8%8D%E6%96%AD%E6%97%8B%E8%BD%AC%E7%9A%84%E5%85%AB%E5%8D%A6%E5%9B%BE%2F</url>
<content type="text"><![CDATA[制作一个不断旋转的八卦图。快速预览代码及效果,点击:八卦图]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用命令行Git在本地创建一个库并上传到Github]]></title>
<url>%2F2018%2F04%2F04%2F%E7%94%A8%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%9C%A8%E6%9C%AC%E5%9C%B0%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%BA%93%E5%B9%B6%E4%B8%8A%E4%BC%A0%E5%88%B0Github%2F</url>
<content type="text"><![CDATA[如何在本地创建一个仓库并上传到github?如何获取一个SSH key? 1 如何获取一个SSH key 为什么要获取SSH key?—因为利用SSH key可以访问你的所有的仓库。 一台电脑需要几个SSH key?—每台电脑只需要一个。 怎么获取SSH key?—可参照如下步骤: 登录GitHub 点击页面右上角的头像 选择Setting 选择SSH and GPG keys 点击generating SSH keys 点击Generating a new SSH key and adding it to the ssh-agent 复制Generating a new SSH key的第一条黑色的命令ssh-keygen -t rsa -b 4096 -C "[email protected]"到GitBash(或终端) 回车三次后得到一个类似泡泡的东西就说明成功了 接着输入cat ~/.ssh/id_rsa.pub,得到一大串英文,将这段英文复制 回到第4步的页面中,点击右上角的绿色按钮”New SSH key” 将刚刚复制的东西放到Key下面的文本框,随便编辑一个Title,点击下面的绿色按钮确认添加 回到Git Bash(终端),运行ssh -T [email protected]测试是否成功,得到一个提示让你回复yes/no,输入yes回车 如果得到”Permission denied(publickey)”,很遗憾,你失败了,需要从第一步开始重新;如果得到的语句里有”You‘ve successfully authenticated”,那么恭喜,你成功了 Tips:ls -al ~/.shh检查本地是否已生成过shh key,如果你已有ssh key,需要重新添加,可在进行以上步骤前在Git Bash(终端)运行rm -rf ~/.ssh/*将现有的ssh key都删掉。 已踩的坑: 在”ssh -T [email protected]”时,遇到如左括号里的代码,一般情况下,输入”ping github.com”即可解决。(错误提示:ssh: Could not resolve hostname github.com: nodename nor servname provided, or not known) 输入”git remote add origin [email protected]:Nolaaaaa/yyy.git”时遇到如左括号里的代码,输入”git remote rm origin”后再重新按步骤输”git remote add origin [email protected]:Nolaaaaa/yyy.git””git push -u origin master”即可。(错误提示:fatal: remote origin already exists.) 2 如何在本地创建一个仓库并上传到github?12345678910111213141516$ mkdir blog //在桌面上创建一个叫"blog"的目录 $ cd blog //"cd blog"进入目录 $ git init //"git init"即在目录"blog"中创建一个仓库(使用"ls -la"可查看)// Initialized empty Git repository in /Users/nola/Desktop/blog/.git/$ touch index.html //"touch index.html"即在目录"blog"中创建一个叫"index.html"的文件$ git status -sb // "git status -sb"用于查看文件的变动,如下"??"表示存在变动,在问你如何处理变动// ?? index.html$ git add index.html //"git add index.html"把变动即新加的"index.html"文件添加到暂存区$ git status -sb //"git status -sb"再次查看文件的变动,绿色的"A"表示添加新加的文件到仓库// A index.html$ git commit -m "我的第一次提交" //"git commit -m"即正式将暂存区的文件提交到本地仓库,即第三步建立的".git"仓库中// \[master (root-commit) be29eb7\] 我的第一次提交// 1 file changed, 0 insertions(+), 0 deletions(-)// create mode 100644 index.html $ git pull //下载github的更新到本地 $ git push //上传到github ps:如果add错想撤销add的内容,可使用”git reset HEAD 文件名”;如果add错又commit了,可使用”reset –hard HEAD^”; 3 GIT工作流12345678910111213// 其中 branchName 是自己本地的分支名字,originBranchName 是远程的分支名字(不能设置为 master/dev ,会覆盖掉远程的代码)$ git fetch --prune // git fetch 相当于是从远程获取最新到本地,并删除远程已经不存在的分支,不会自动merge$ git checkout -b [branchName] origin/dev // 基于远程的dev分支,创建一个本地分支// 进行开发...$ git add . $ git commit -m "update"// 开发完成...$ git fetch --prune // 再次获取远程最新的代码$ git merge origin/dev // 把本地的代码和远程的dev分支代码进行合并$ git commit -m "解决冲突" //如果本地和远程都修改过相同代码在这一步需要先解决冲$ git push origin [branchName]:[originBranchName] // 把本地的 branchName 推送到远程的 originBranchName 分支上// 最后一步,在码云上发起Pull Requeste请求,源分支是 originBranchName,目标分支是 dev// Pull Requeste 可以把多个提交点压缩成一个提交点,如果直接使用 git push 之前需要使用 git rebase 能把多个提交点压缩成一个 4 远程仓库名称改变后修改本地的对应仓库名123456$ git remote -v //列出远程仓库信息//方法一$ git remote set-url origin [email protected]:username/reponame.git //修改远程仓库对应网址//方法二$ git remote rm origin //本地仓库删除远程仓库$ git remote add origin [email protected]:username/reponame.git //添加新的远程仓库 5 命令行统计自己的代码行数1$ git log --author="yourname" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' - 6 修改git 用户名和邮箱12345678910//修改当前project:$ git config user.name '用户名'$ git config user.email '邮箱名'修改全局:$ git config --global user.name '用户名'$ git config --global user.email '邮箱名'// 打开.gitconfig文件:vi ~/.gitconfig 7 撤销 commit1git reset --soft HEAD^ 8 解决github文件夹灰色不能点开的问题12345678// 本地库中,删掉灰色文件夹,保存在别的地方,提交本地文件:$ git add .$ git commit -m 'update'$ git push // 把文件夹重新添加进去并重新提交本地文件:$ git add *$ git commit -m 'update'$ git push 9 git add . 和 git add * 的区别:12$ git add * git会自动把你当前目录所有修改过的文件添加$ git add . git会递归地将你执行命令时所在的目录中的所有文件添加上去 检查已有的配置信息1$ git config --list 给git配置别名12// 给 commit 起一个别名 ci$ git config --global alias.ci commit]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hexo--Blog Beautify]]></title>
<url>%2F2018%2F04%2F03%2FBlog-Beautify%2F</url>
<content type="text"><![CDATA[这篇的目的是记录美化博客的过程Ps:$ hexo generate和$ hexo deploy两句长长的语句可以简写成一句$ hexo d -g,使用一个内容为<!-- more -->的闭合escape标签可以让首页只展示这个标签前面的部分 本博客美化的内容有:1 改变页面主题并添加动画2 改变博客字体大小3 给博客添加icon4 添加作者的头像5 去掉博客底部的Powered by6 给文章添加分享功能7 给博客添加评论功能8 给博客添加阅读量 1 改变主题并添加动画 改变主题,可在主题配置文件_config.yml中搜索Scheme Settings,四个主题中,去掉想要的主题前面的井号注释。Scheme 是 NexT 提供的一种特性,借助于 Scheme,NexT 为你提供多种不同的外观。同时,几乎所有的配置都可以 在 Scheme 之间共用。目前 NexT 支持三种 Scheme,其中: Muse - 默认 Scheme,这是 NexT 最初的版本,黑白主调,大量留白 Mist - Muse 的紧凑版本,整洁有序的单栏外观 Pisces - 双栏 Scheme,小家碧玉似的清新 Gemini - 左侧网站信息及目录,块+片段结构布局 添加背景动画,可在主题配置文件_config.yml中搜索canvas_nest,然后在想要的动画后把false改成true 1234canvas_nest: falsethree_waves: falsecanvas_lines: truecanvas_sphere: false 2 改变博客字体大小 可以在/themes/(主题名)/source/css/_variables/base.styl中修改 也可以在主题配置文件_config.yml中搜索font修改 3 给博客添加icon 找到想要的图,把图片放在/themes/(主题名)/source/images路径中,并在主题配置文件_config.yml中搜favicon1234favicon: small: /images/icon.png medium: /images/icon.png apple_touch_icon: /images/icon.png 4 添加作者的头像 找到想要的图,把图片放在/themes/(主题名)/source/images路径,并在主题配置文件_config.yml中搜avataravatar: /images/head.jpg 5 去掉博客底部的Powered by 方法一:在主题配置文件_config.yml中搜索theme,把如下两个值改成false 123theme: enable: false version: false 方法二:用VScode打开如下路径下的文件,改变代码内容/themes/(主题名)/layout/_partials/footer.swig 6 给文章添加分享功能 在主题配置文件_config.yml中搜索share123456789needmoreshare2: enable: true postbottom: enable: ture options: iconStyle: default boxForm: horizontal position: bottom + Left networks: Weibo,Wechat,QQZone,Evernote,Twitter,Facebook 7 给博客添加评论功能 进入leanCloud网站——注册(校验邮箱)——创建应用(不用选什么直接点创建,当然有钱也可以点商用的,随便花,反正我没钱) 设置——应用key——复制App ID & App Key放到主题配置文件_config.yml中(搜索valine)(并设置enable为true) 1234valine: enable: true appid: appkey: 设置——安全域名——在Web 安全域名处添加博客的域名 在hexo路径的终端中输入npm install valine --save 可以去leanCloud管理控制台查看,储存——comment处可查看评论,也可以修改喔 8 给博客添加阅读量 和添加评论功能使用同一个网站,在主题配置文件中的设置不同,主题配置文件_config.yml中搜索leancloud_visitors1234leancloud_visitors: enable: ture app_id: app_key:]]></content>
<categories>
<category>备忘</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[命令行入门]]></title>
<url>%2F2018%2F04%2F03%2F%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[今天开始慢慢把博客从博客园转到这里,而且发现了一个非常有用的能将html转成markdown格式的在线工具html转markdown - 在线工具,效果还是不错,打call 常见的Linux命令总结文件和目录 操作 命令 备注 进入目录 cd 显示当前目录 pwd 我是谁 whoami 查看隐藏文件 ls -a 路径 查看文件信息 ls -l 路径 查看目录结构 tree windows不支持 创建目录 mkdir 目录名 目录名 创建多个嵌套目录 mkdir -p 目录名/目录名 删除目录 rmdir 目录名 目录名 删除多个嵌套目录 rmdir -p 目录名/目录名 创建文件 touch 文件名 创建文件 echo '内容' > 文件名 强制创建文件 echo '内容' >! 文件名 追加文件内容 echo '内容' >> 文件名 删除文件目录 rm -rf 文件名或目录名 -f强制、-r递归、-i提示 重命名文件或目录 mv 原名 新名字 移动文件或目录 mv 文件或目录名 路径 复制文件 cp 文件名 路径 复制文件或目录 cp -r 文件或目录名 路径 文件名查找文件 find 路径 -name "文件名" 正确输出重定向 find 路径 -name "文件名" >或>> 指定文件名 >替换内容、>>追加内容 错误输出重定向 find 路径 -name "文件名" 2>或2>> 指定文件名 错误内容扔掉:指定文件名改成/dev/null 所有输出重定向 find 路径 -name "文件名" &>或&>> 指定文件名 内容查找文件 grep "内容" -rn 目录或文件名 -r递归搜索,-n显示行号 查看文件内容 cat 文件名 合并文件内容 cat 文件名 文件名 合并文件内容到新文件 cat 文件名 文件名 > 新文件名 下载文件 curl -L 地址 > 文件名 拷贝网页 wget -p -H -e robots=off 地址 整个网页所有文件 压缩文件 gzip 文件名 解压文件 gunzip 文件名.gz 打包目录 tar -czvf 目录名.tar.gz ./目录名 解包目录 tar -xzvf ./目录名.tar.gz 磁盘占用 df -kh 当前目录大小 du -sh . 文件大小 du -h 用户和权限 操作 命令 备注 开启root权限 sudo 正常操作 切换成root用户 sudo -i exit 退出用户登录 查看文件类型 ls -l d目录、-普通、l链接、c/b设备文件 查看文件权限管理 ls -l r读、w写、x执行 查看权限分组 ls -l d rwx rw- r-- 文件类型 用户 组 其他 修改权限 chomod 身份加减权限 文件或目录名 身份:u用户、g组、o其他;符号:+添加权限、-删除权限;权限:r`wx` 各种符号代表的含义 ~:用户目录:我的电脑上就是/users/nola/,根目录,一个/就是根目录,不管你有多少个硬盘,他会把所有硬盘联合起来当作一块 .:一个点表示当前目录 ..:两个点表示父目录 $:意思是可以输入命令,没有实际意义 /:根目录 *:0到无穷多个 ?:任意1个 TAB键可以补全文件名或者目录 如何使用explainshell.com这个网站? explainshell.com是一个帮助你解释命令行的网站,它会分析命令的帮助文档,然后根据你想查询的命令参数来抽取对应的解释,然后简洁易懂地显示出来。使用这个网站工具来查看命令式如何工作可以避免不必要的错误出现,同时也是一个很好的学习命令的方式。 这个网站使用很简单,首先,点击explainshell.com打开网站,网站界面如下图,在中间的对话框输入你想要查询的命令,点击确认后,即可在下方看到关于这个命令每一个字符字母的解释(具体我就不说怎么输入点击及看图了,这么简单的页面感觉傻子都能看懂)。 如果你想说,这个界面是英文的,我不懂英文怎么办啊?别慌,还有三种办法可以选择,1.放弃前端 2.现在开始好好学英文 3.乖乖下载一个词典吧。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hexo--Build Blog]]></title>
<url>%2F2018%2F04%2F02%2Fbuild-blog%2F</url>
<content type="text"><![CDATA[因为想拥有一个独属于自己的个人博客啊 1 安装部署hexo 进入一个安全的目录,cd ~/Desktop 在 GitHub 上新建一个空 repo,repo 名称是「你的GitHub用户名.github.io」 安装 Hexo $ npm install -g hexo-cli $ hexo init myBlog 新建一个网站 $ cd myBlog $ npm I $ hexo new这时会看到一个 md 文件的路径 $ vi _config.yml编辑网站配置 把第 6 行的 title 改成你想要的名字 把第 9 行的 author 改成你的名字 把最后一行的 type 改成 type: git 在最后一行后新增一行 repo: 仓库地址 (仓库地址应为「你的GitHub用户.github.io」对应的仓库地址,仓库地址以 [email protected] 开头) ⚠️repo后面有个空格。 $ npm install hexo-deployer-git —save安装 git 部署插件 $ hexo deploy 进入「你的GitHub用户名.github.io」对应的 repo,打开GitHub Pages功能,如果已经打开了,直接点击即可预览链接博客 2 添加新的博客 $ hexo new 添加博客md,这时会出现一个路径,复制显示的路径,打开编辑 $ hexo generate生成静态文件 $ hexo deploy 部署网站 hexo clean 清除缓存文件 (db.json) 和 public 文件夹下已生成的静态文件 3 更换博客的主题 进入喜欢的主题的 GitHub 首页 复制它的 SSH 地址或 HTTPS 地址 根据md文件提示下载 将myBlo文件夹中中_config.yml的第 75 行改为 theme: 新下载的主题的文件夹名字 $ hexo generate $ hexo deploy 4 上传博客源代码注意⚠️:「你的Github用户名.github.io」上保存的只是你的博客,并没有保存「生成博客的程序代码」,所以需要再创建一个名为 blog-generator的空仓库,用来保存 myBlog 里面的「生成博客的程序代码」。 在 GitHub 创建 blog-generator 空仓库 按照截图中的命令执行即可,记住不要用 HTTPS 地址。 这样你的博客发布在了「你的Github用户名.github.io」而「生成博客的程序代码」发布在了 blog-generator。所有数据万无一失。 以后每次 hexo deploy 完之后,博客就会更新;然后还要 add / commit /push 一下「生成博客的程序代码」,以防万一。 这个 blog-generator就是用来生成博客的程序,而「你的Github用户名.github.io」仓库就是你的博客页面。]]></content>
<categories>
<category>备忘</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2F2018%2F04%2F01%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
<entry>
<title><![CDATA[村上春树--眠]]></title>
<url>%2F2018%2F03%2F04%2F%E7%9C%A0%2F</url>
<content type="text"><![CDATA[我是企待睡去的肉体,也是行将醒来的意识。 我仅仅是睡不着。彻夜无眠。可是除了睡不着的事实,我处于极其正常的状态。我根本不困,神志也清醒如常。甚至可以说比平常更清醒。身体也毫无异常之处。还有食欲。并不觉得疲劳。从实际观点来看不存在任何问题,也没有不便。只是睡不着罢了。 每天差不多都是相同的重复。昨天和前天颠倒顺序,也没有任何不便。我不时想,这叫什么人生啊!但也没有因此感觉光阴虚度。我仅仅是感到惊讶,惊讶于昨天与前天毫无区别,惊讶于自己被编排入这样的人生,惊讶于自己留下的足迹甚至还未及认清,就在转瞬间被风吹走,变得无影无踪。 没有专注力的人生,就仿佛大睁着双眼却什么也看不见。 是的,我名副其实地生活在睡眠中。在我的周遭,在我的内部,一切东西都凝滞而沉重,阴沉而混沌。就连自己生存于这个世界的状态,都像是不牢靠的幻觉。 我是企待睡去肉体,也是行将醒来的意识。 在夜的黑暗中,我一直醒觉如昼。甚至不会思考。聆听着时钟镌刻时间的声音,我唯有凝望黑暗一点点变深,再一点点变淡… 所谓死,很可能是和睡眠之类性质截然不同的状况,它也许就是此时此刻我眼前看到的深邃无涯的清醒的黑暗。所谓死,也许就是在这种黑暗中保持永恒的清醒。 然而随着春天到来,户外的光线渐渐变得明亮,我能感觉心中迄今冻得僵硬的东西,一点点地开始变软、融化。 那一切都明明白白栩栩如生,你压根儿想不到是梦。 阅读的时候其实颇受感动,可结果脑子里居然什么也没留下。理应存在的感情的震颤与亢奋的记忆,曾几何时悄然脱落,踪影全无。即便如此,那时我在书上消耗掉的庞大的时间究竟又算什么? 意识曾几何时游离我的肉体而去。世界无声地摇颤,将形形色色的东西抖落在地。 他们一无所知,深信世界毫无变化照常运转。其实并非如此,世界正在他们并不知晓的地方发生变化,直至无可挽回。 待回过神来,唯有时间白白流逝不返,书页却几乎原封未动。 人在睡眠里自然松弛使用过多的肌肉,镇定使用过多的思维回路,并且释放体内电能,人就是这样得以冷却的。睡眠是宿命般被编入人这一系统的程序中的行为,谁都不能避免。假如失去睡眠,人就将失去存在的基础。 昨天和前天颠倒顺序,也没有任何不便。 直到天快亮,总算有一缕睡意前来造访。我的指尖似乎微微触摸到睡眠的边缘。然而就在一层薄墙之隔的邻室,我的意识却清醒无比,在凝目守望着我,我的肉体蹒跚地彷徨在微明中,却又始终感觉自身意识的视线与气息近在身畔。我是企待睡去的肉体,也是行将醒来的意识。 这才是本来的我应有的姿态,我想。重要的是专注力,我这么想道。没有专注力的人生,就仿佛大睁着双眼却什么也看不见。 我的身体几乎是在自动运转,大脑却浮游在别的空间。我不假思索地做家务,拿零食给孩子迟,与丈夫说话。 它也许就是此时此刻我眼前看到的深邃无涯的清醒的黑暗。所谓死,也许就是在这种黑暗中保持永恒的清醒。 白日里,我时常浑浑噩噩,仿佛大脑蒙着一层薄膜,无法辨清事物的正确距离、质量和触感。而且每隔一定的间歇,柔软的缺漏便如同弛缓的波浪涌流而至。 是的,我名副其实地生活在睡眠中。在我的周遭,在我的内部,一切东西都凝滞而沉重,阴沉而混浊。就连自己生存于这个世界的状态,都像是不牢靠的幻觉。似乎只要刮起一阵强风,我的肉体就将被吹到世界尽头,吹到天涯海角见所未见闻所未闻的土地。而我的肉体将在那里与我的意识永远分离。所以我很想牢牢抓住某样东西。然而纵目四望,周边却看不到一样可以抓牢的事物。 每到夜间,猛烈的清醒便倏然而至。在这清醒面前,我束手无策。我被强大的力量牢牢固定在清醒的核心。那力量过于强大,我只能始终清醒着直至天亮。在夜的黑暗中,我一直醒觉如昼。甚至不会思考。聆听着时钟镌刻时间的声音,我唯有凝望黑暗一点点变深,再一点点变淡。 我孤身一人,被关在这小铁箱里,无处可逃。现在是黑暗最深沉的时刻,男人们仍在摇撼我的汽车。]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
<entry>
<title><![CDATA[托尔斯泰--安娜·卡列尼娜]]></title>
<url>%2F2018%2F03%2F04%2F%E6%89%98%E5%B0%94%E6%96%AF%E6%B3%B0-%E5%AE%89%E5%A8%9C%C2%B7%E5%8D%A1%E5%88%97%E5%B0%BC%E5%A8%9C%2F</url>
<content type="text"><![CDATA[安娜·卡列尼娜 幸福的家庭都相似,不幸的家庭各有各的不幸。 如果这就是文明的目的,那我宁可做个野蛮人。 “不是我可以将功赎罪,而是凭你的慈爱饶恕我。” 我若能克制尘世欲望,那当然无比高尚;我若忍耐不了这寂寞,毕竟也享尽人间欢乐! 人们往往把欲望的满足看成幸福。 人生的一切变化,一切魅力,一切美好都是由光明的阴影构成的。 人并不是因为美丽才可爱,而是因为可爱才美丽。 “我需要爱情,可是没有爱情,因此一切都完了。”]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
<entry>
<title><![CDATA[道金斯--自私的基因]]></title>
<url>%2F2018%2F03%2F04%2F%E8%87%AA%E7%A7%81%E7%9A%84%E5%9F%BA%E5%9B%A0%2F</url>
<content type="text"><![CDATA[作者:理查德·道金斯 基因的定义是:染色体物质的任何一部分,它能够作为自然选择的单位对连续若干代起作用。 我们以及其他一切动物都是各自基因创造的机器。 随着时间的推移,复制基因为了保证自己在世界上得以存在下去而采用的技巧和计谋也逐渐改进,但这种改进有没有止境呢?用以改良的时间是无穷无尽的。一千年的变化会产生什么样的怪诞的自我保存机器呢?经过四十亿年,古代的复制基因又会有什么样的命运呢?它们没有消失,因为它们是掌握生存艺术的老手。但在今日,别以为它们还会浮游于海洋之中了。很久以前,它们已经放弃了这种自由自在的生活方式了。在今天,它们群集相处,安稳地寄居在庞大的步履蹒跚的“机器”人体内,与外界隔开来,通过迂回曲折的间接途径与外部世界联系,并通过遥控操纵外部世界。它们存在于你和我的躯体内;它们创造了我们,创造了我们的肉体和心灵;而保存它们正是我们存在的终极理由。这些复制基因源远流长。 生物体是基因创造的生存机器。生命短暂,基因不朽。 成功的基因的一个突出特性是其无情的自私性。这种基因的自私性通常会导致个体行为的自私性。然而我们也会看到,基因为了更有效地达到其自私的目的,在某些特殊情况下,也会滋长一种有限的利他主义。上面一句话中,”特殊”和”有限”是两个重要的词儿。尽管我们对这种情况可能觉得难以置信,但对整个物种来说,普遍的爱和普遍的利益在进化论上简直是毫无意义的概念。 一个明显的利他性行为表面看去似乎(不管可能性何其小)使利他主义者有较大的可能死亡,而受益者有较大的可能生存下来。更仔细地观察一下,我们常常会发现明显的利他性行为实际上是伪装起来的自私行为。 任何利他系统都有先天的不稳定性,因为自私的个体会滥用它,随时准备利用它。 我们必须把利他主义的美德灌输到我们子女的头脑中去,因为我们不能指望他们的本性里有利他主义的成分。 自私的本源来自生命本身,个体的不安全感和存活的需求指导我们必须靠近一个更优秀的灵魂和肉体,这让所谓一陈不变的爱情和所谓专一的选择看起来极不真实。 由于文化的出现,我们的人生的终极任务,将不只是繁殖,还有文化的创造和传承。 我们不过是基因的载体,所有物质的生命,不过是基因为了延续和进化这个目的而存在的。从这个意义上讲,我们的生命真的没有那么重要。在我们有限的生命中,如果能够将遗传的信息传递下去,再将创造出的信息、知识流传下去,我们的生命已经相当完美了。 亲代投资的定义:亲代对子代个体进行任何形式的投资,从而增加了该生物个体生存的机会,因而它们得以成功地繁殖,但是,这些以牺牲亲代对子代其他个体进行投资的能力为代价。 生育太多子女的基因根本不会大量地传递給下一代,因为带有这种基因的幼儿极少能活到成年。 因为自然界没有福利国家,所以没有必要在此出生率上作利他的抑制,任何放纵的基因会立即受到惩罚,含有那样基因的孩子就会挨饿。 我们都高度地希冀全世界成为一个福利国家,但是你不能拥有一个不自然的福利国家,除非你也拥有一个不自然的生育控制方法,否则最终的结果比自然演化还要惨。福利国家也许是动物界已知最大的利他系统,但是任何利他系统都有先天的不稳定性,因为它的开放性给自私的个体以滥用的可能,而自私的个体也随时准备利用和剥削它。 生物个体的最好赌注是,暂时自我克制,期待更好的时机。即使这个时刻永远也不会到来,最终落得身无后代。 自然选择往往有利于表现的具有欺诈行为的幼雏。 人的生活方式在很大程度上取决于文化而不是基因。然而,更可能的是,男人大多倾向于杂交,女人大多倾向于一夫一妻。根据生物进化的理论,我们也可以预见到这两种倾向。在一些具体的社会里,哪一种倾向占上风取决于具体的文化环境,正如在不同的动物物种中,要取决于具体的生态环境一样。 从广义上来说,模仿是拟子复制的方式。 一个“思想的拟子”可以定义为:可以从某个人的头脑传到另一个人头脑中的事物。 拟子和基因常常互相支持,但是它们有时也会对立。例如,单身生活的倾向与基因的遗传是不相符合的,独身生活的基因在基因库中注定是要失败的,除非在某些特别的环境中,如我们在群居的昆虫身上看到的;但是独身生活的拟子却可以在拟子库内存活很长时间。 我们出生即成为生物基因的机器,并且被文化陶冶成为拟子的机器,但是我们还是有能力反抗我们的缔造者,每个人都可以反叛自己的复制者施加给我们的暴虐。]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
<entry>
<title><![CDATA[芒克的诗]]></title>
<url>%2F2018%2F03%2F04%2F%E8%8A%92%E5%85%8B%E7%9A%84%E8%AF%97%2F</url>
<content type="text"><![CDATA[醒来,是你孤零零的脑袋。 城市1 醒来是你孤零零的脑袋夜深了,风还在街上象个迷路的孩子东奔西撞。 2 街被折磨得软弱无力地躺着。而流着唾液的大黑猫饥饿地哭叫。 3 这城市痛苦得东倒西歪,在黑暗中显得苍白。 4 沉睡的天,你的头发被黑夜揉得凌乱。我被你搅得彻夜不眠。 5 当天空中垂下了一缕阳光柔软的头发,城市浸透了东方的豪华。 … 晚年墙壁已爬满皱纹墙壁就如同一面镜子一个老人从中看到一位老人屋子里静悄悄的。没有钟听不到嘀嗒声。屋子里静悄悄的。但是那位老人他却似乎一直在倾听什么也许,人活到了这般年岁就能够听到——时间——他就像是个屠夫在暗地里不停地磨刀子的声音他似乎一直在倾听着什么他在听着什么他到底听到了什么 同谋自由不过是猎人和猎物之间的距离 把眼睛闭上把眼睛闭上把自己埋葬这样你就会与世隔绝你就不会再感到悲伤噢,我们这些人啊我们无非是这般下场你是从黑暗中来的你还将在黑暗中化为乌有 老房子那屋顶那破旧的帽子它已戴了很多年虽然那顶帽子也曾被风的刷子刷过但最终还是从污垢里钻出了草它每日坐在街旁它从不对谁说什么它只是用它那让人揣摸不透的眼神看着过往的行人它面无光泽它神情忧郁那是因为它常常听到它的那些儿女总是对它不满地唠叨]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
<entry>
<title><![CDATA[大仲马--基督山伯爵]]></title>
<url>%2F2018%2F03%2F04%2F%E5%9F%BA%E7%9D%A3%E5%B1%B1%E4%BC%AF%E7%88%B5%2F</url>
<content type="text"><![CDATA[基督山伯爵 精神上的创伤就有这种特性,——它可以被掩盖起来,但绝不会收口;它是永远痛苦,永远一被触及就会流血,永远鲜血淋淋的留在心头。 人生何所求,致富和自由。 人的天性生来不适宜欢乐,只会紧紧地抱住痛苦。 人类的司法正义不足以抚平心灵的创伤,它至多只能做到以命抵命。 在这个世界上既无所谓幸福,也无所谓不幸,只有一种状态和另一种状态的比较。如此而已。只有体验过极度不幸的人,才能品尝到极度的幸福。只有下过死的决心的人,才能懂得活着有多美好。 我从来不关心别人,我也从来不想保护社会,既然社会并不保护我,进而言之,这个社会关注我,通常也是为了损害我;因此,对他人和社会,我不再尊重,但是保持中立,那么他人和社会就已经亏欠我了。 一个人夺走了您的恋人,引诱了您的娇妻,玷污了您的爱女,使您蒙受奇耻大辱,终生忧心惨切,痛苦莫状,无权享受上帝造人所赐予人的那份幸福,难道您一剑刺穿那人的胸膛,或者一枪击中他的脑袋,就认为报仇雪恨了吗?岂有此理!…… 如果别人给我造成一种缓慢的、深切的、无边而又恒久的痛苦,那么我想报复,就要尽量给对方造成相同的痛苦,就是东方人所说的以眼还眼以牙还牙。 别了,我要回去,远远离开这相互残杀的人类 在这群老百姓看来,处决犯人不过是狂欢节的序幕。 牵两只羊给屠夫送去,牵两条牛进屠宰场,然后让其中一只两只明白它的同伴不会死,瞧吧,羊会咩咩欢叫,牛也会哞哞欢叫。可是人呢,上帝照他自己的形状创造出来的人,给人规定仁爱为第一要义,赋予人以声音表达意思;然而,他听到他的同类人得救的消息,第一声喊叫是什么!是一声咒骂!人这个自然的杰作,这个万物之灵,该有多么光彩啊! “现在我明白伯爵先生的话了,人一旦看惯了这种场面,再看见别的什么就不再动心了。” “还有一点很重要:唯有在这种时候,才能研究人的性格。”伯爵说道,“一踏上断头台的阶梯,死亡就摘掉了人一生所戴的面具,本相暴露无遗。” 聪明的人,不该知道的绝不多问,不愿相信的一概不信。 “哼!幸福,谁说的准?幸不幸福,是四堵墙壁里的秘密,虽说墙壁有耳,但没有舌头。如果要说腰缠万贯就能幸福,那么丹格拉尔就算是幸福的人了。” – 卡德鲁斯]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
<entry>
<title><![CDATA[纪伯伦的诗]]></title>
<url>%2F2018%2F03%2F04%2F%E7%BA%AA%E4%BC%AF%E4%BC%A6%E7%9A%84%E8%AF%97%2F</url>
<content type="text"><![CDATA[纪伯伦的诗 沙与沫我曾经七次鄙视自己的灵魂第一次,当它本可进取时,却故作谦卑;第二次,当它在空虚时,用爱欲来填充;第三次,在困难和容易之间,它选择了容易;第四次,它犯了错,却借由别人也会犯错来宽慰自己;第五次,它自由软弱,却把它认为是生命的坚韧;第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。 我们活着只为的是去发现美。其他一切都是等待的种种形式。 如果你希望拥有,那么切记苛求。 先知生命的确是黑暗的,除非是有了激励;一切的激励都是盲目的,除非是有了知识;一切的知识都是徒然的,除非是有了梦想;一切的梦想都是虚空的,除非是有了爱。]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
<entry>
<title><![CDATA[太宰治式人生]]></title>
<url>%2F2018%2F03%2F04%2F%E5%A4%AA%E5%AE%B0%E6%B2%BB%E5%BC%8F%E4%BA%BA%E7%94%9F%2F</url>
<content type="text"><![CDATA[生而为人,我很抱歉。 人间失格 对同类的极度恐惧,反而更加期盼能够亲眼见识令人可畏的妖怪,越是神经质,越是胆怯的人,越是期盼着强犷风暴的到来。 懦夫,连幸福都害怕,碰到棉花也会让他受伤,他甚至会被幸福所伤。 日复一日重复相同的事,依照惯例与昨日差别全无,只要能避开粗俗的狂喜,自然也不会有大悲降临,阻塞去路的绊脚石,蟾蜍将会迂回绕开而行。 – Cros·Guy·Charles 尽管在过往的人生中,我曾无数次希望有人能杀了我,但我从未想过要杀人。因为面对可怕的对手,我反而只想着要如何让对方幸福。 我知道有人是爱我的,但我好像缺乏爱人的能力。 所谓的世间,不就是你吗? 我想哭,可是,眼泪已经流不出来了。幸福感,就是沉入悲哀之河的河堤的那些闪着微光的金砂,就是那种感觉吧。经历过悲伤的极限,心情不可思议地,朦胧地明亮起来。假如这就是幸福的感觉,那么我现在确实是幸福的。 因为怯懦,所以逃避生命,以不抵抗在最黑暗的沉沦中生出骄傲,因为骄傲,所以不选择生,所以拒斥粗鄙的乐观主义。 胆小鬼连幸福都会害怕,碰到棉花都会受伤,有时还会被幸福所伤。在还没受伤之前,焦虑的想要尽早保持原状的分开,并散布着与往常一般自娱娱人的烟雾。 相互轻蔑却又彼此来往,并一起自我作贱——这就是世上所谓“朋友”的真面目。 我急切地盼望着可以经历一场放纵的快乐,纵使巨大的悲哀将接踵而至,我也在所不惜。 越发渴望“自由”,以致想要轻声啜泣 唯有尽力自持,方不致癫狂。 人是不可能一边攥紧拳头一边微笑的,唯有猴子才会那样。 我仍然认为向人诉苦不过是徒劳,与其如此,不如默默承受。 我还是搞不懂,愈想愈迷糊,这令我益发感到惶惑不安,仿佛这世上只有我是异类。我几乎无法和旁人交谈,因为我不知道该说什么才好。于是我想到一个好方法,那就是搞笑。这是我对人类最后的求爱。尽管我对人类满腹恐惧,但却怎么也没法对人类死心。 并且,我依靠逗笑这一根细线保持住了与人类的一丝联系。表面上我不断地强装出笑脸,可内心里却是对人类拼死拼活的服务,汗流浃背的服务。 一旦别人问起自己想要什么,那一刹那反倒什么都不想要了。怎么样都行,反正不可能有什么让我快乐的东西——这种想法陡然掠过我的脑海。 异乡是个反比故乡更轻松的场所。 东京八景 我沉溺在自己的苦恼中,却忽视了别人也在努力活着的事实。 我觉得不自在起来,想要把自己现如今的生活,用棍子打个粉碎。 在这样无趣的平原上,许许多多的人从日本全国各地蜂拥而至,汗流浃背相互推挤,竞争每一吋土地,一喜一忧、相互嫉妒、反目,雌的呼唤雄的,而雄的只是近乎狂乱的走来走去。 你却一人因为无知的自信而疲倦,真是太差劲了。 逃跑是卑鄙的,努力想象个罪孽深重的小孩般死去。 我今年三十二岁。就日本的伦理来说,这个年龄已经是将要进入中年的阶段。还有,即使我尝试着寻找自己肉体、热情,也无法否定这个悲伤的事实。先记下是好的,因为你已经失去青春了。 活下去的理由全然一个也没有,决心以一个愚蠢的灭亡的百姓的身份自杀,决心忠实上演时代潮流分配给你我的角色,一定会输给别人的悲哀卑屈的角色。 那家伙也逐渐变成俗物了。那无知的背后坏话,随着微风一起轻轻飘到了我的耳朵。我每次都在心中强烈的回答:我从一开始就是个俗物,只是大家没发现而已。这是反抗,当打算以文学为一生的事业时,愚蠢的人反而会轻蔑的看我,我只能置之一笑。永远年轻是演员的世界,在文学则没有 很多时候,人会不知不觉得走在错误的原野上。 现在不是一味地恍惚、不安的复杂叹息,在狭窄的房间里里中走来走去的时候,我一定得不断的前进才行。 女生徒 “我好爱这世界!”我热泪盈眶地想。注视着天空,天空慢慢改变,渐渐变成了青色。我不停地叹息,好想褪去自己的衣裳。就在这时候,树叶、草变得透明,已看不见它们的美丽,我轻轻触摸草地。好想美丽地活下去。 一早,睁开眼睛的心情是很有趣的。好像玩捉迷藏时,动也不动地躲在漆黑的壁橱中,突然,嘎啦一声门被人拉开,光线倏地照射进来,然后听到对方大声叫道:“找到你了!”好刺眼,然后一阵怪异的感觉,心心口扑通扑通地直跳,就像那种抓着和服前襟,略带羞涩地从壁橱里出来,然后气呼呼的感觉。不、不对,不是这种感觉,应该是更让人受不了得感觉;好像打开一个箱子,结果里面还有个小箱子,把小箱子打开,里里面又有个小箱子,继续打开,又有箱子,再打开,还有箱子,然后,七八个箱子,全部打开后,才停止这场没完没了,最后出现了一个骰子般大小的箱子,轻轻地把它打开来一看,里面却空荡荡。有点接近这样的感觉。 “女人之所以热爱茶碗、收藏漂亮花纹的和服,就是因为只有那些东西才是真正的生存价值。每一刻的行动,都是活在当下的目的。此外,还需要什么呢?高深的现实,完全地抑制住女人的悖德与超然,若能让这些渴望直率地表现出来自我与身体,不知道会有多轻松,但对于心中女人这个深不可测的“恶魔”,每个人都不愿碰触,装作没看到,正因如此,发生了许多悲剧。也许只有高深的现实才能真正地拯救我们。” 只要与人见面,一说出“近来可好?”“天气变冷了!”之类的问候,不知道为什么,就会痛苦地觉得自己像个世上仅有的骗子,好想就此死去。最后,对方也对我戒慎恐惧地不痛不痒地寒暄、说些净是谎言的感想。一听到这些,不但会因为对方吝于关心而感到悲伤,自己也越来越讨厌这个世界。世人,难道就是彼此这样呆板地招呼、虚伪地关怀,到双方都精疲力竭为止,就此度过一生吗? 在这世上,说不定你的生存方式是正确的,但是,我就是无法那样地活下去。 我难过地想哭,看来因同情、怜悯而结婚是个错误,也许一个人生活会比较好。 “我好爱这世界!”我热泪盈眶地想。注视着天空,天空慢慢改变,渐渐变成了青色。我不停地叹息,好想褪去自己的衣裳。就在这时候,树叶、草变得透明,已看不见它们的美丽,我轻轻触摸草地。好想美丽地活下去。 晚年 我本想这个冬日就死去的,可最近拿到一套鼠灰色细条纹的麻质和服,是适合夏天穿的和服,所以我还是先活到夏天吧。 我在无人知晓中变得异常,又在无人知晓中恢复正常。 我们都感受到了那种宛如偷情般的心跳,两个人情绪激动,发表着愚蠢的长篇大论,急于让对方了解自己。我们为许多虚假的言语所感动,频频举杯互敬。 “一直活到秋天的蚊子被称作哀蚊,那是因为有的人大发慈悲不点蚊香的缘故。” “未必会有那种事,不过假如为我立铜像的话,我希望右脚向前迈出半步,昂然挺胸,左手插进西装马甲,右手将写坏的稿子攥成一团,而且不要加上脑袋。不过,这倒没有什么特别的意思,我只是不愿意自己的鼻尖上落满鸟屎。基石上要这样写:这里有一个男人,生来,死去。他把自己的一生都用来撕毁写坏的稿子。” 哥哥说:“我不认为小说无聊。我只是觉得有些啰唆,明明一句话就能说清楚的事却要写上一百页来制造气氛。”我一时难以开口,思索片刻后回答说:“语言当然是越简短越好,前提是要令人信服。” 富岳百景 是一点点的自负,但我却想清楚地拥有这唯一的自负。]]></content>
<categories>
<category>摘录</category>
</categories>
</entry>
</search>