-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
406 lines (288 loc) · 288 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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[string常用函数]]></title>
<url>%2F2017%2F09%2F05%2Fc%E4%B8%8Ec%2B%2B%2Fstring%E5%B8%B8%E7%94%A8%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[string常用函数 string类的构造函数12string(const char *s); // 用s字符串初始化string(int n, char c); // 用c个字符c初始化 string类的字符操作123456const char &operator[] (int n) const;const char &at(int n) const;// []和at()均会返回字符串中第n个字符的位置,但at函数提供范围检查,当越界时会抛出out_of_range异常,下标运算符[]不提供检查访问const char *data() const; //返回一个以非null终止的c字符数组const char *c_str() const; //返回一个以null终止的c字符串int copy(char *s, int n, int pos = 0) const; // 把当前串中以pos开始的n个字符拷贝到以s开始位置的字符数组中,返回实际拷贝的数目 string的特性描述123456int capacity() const; //返回当前容量(即string中不必增加内存即可存放的元素个数)int max_size() const; //返回string对象中可存放的最大字符串的长度int size() const; //返回字符串大小int length() const; //返回当前字符串的长度bool empty() const; //返回字符串是否为空void resize(int len, char c); //把字符串当前大小置为len,并用字符c填充不足的部分 string的子串1string substr(int pos = 0, int n = npos) const; //返回pos开始的n个字符组成的字符串 string的交换1void swap(string &s2); // 交换当前字符串与s2的值 string类的查找函数123456789101112131415161718192021222324252627282930313233343536int find(char c, int pos = 0) const; //从pos开始查找字符c在当前字符串的位置int find(const char *s, int pos = 0) const; //从pos开始查找字符串s在当前串中的位置int find(const char *s, int pos, int n) const; //从pos开始查找字符串s中前n个字符在当前串中的位置int find(const string &s, int pos = 0) const; //从pos开始查找字符串s在当前串中的位置// 查找成功时返回当前所在位置,失败返回string::npos的值int rfind(char c, int pos = npos) const;//从pos开始从后向前查找字符c在当前串中的位置int rfind(const char *s, int pos = npos) const;int rfind(const char *s, int pos, int n = npos) const;int rfind(const string &s,int pos = npos) const;//从pos开始从后向前查找字符串s中前n个字符组成的字符串在当前串中的位置,成功返回所在位置,失败时返回string::npos的值int find_first_of(char c, int pos = 0) const;//从pos开始查找字符c第一次出现的位置int find_first_of(const char *s, int pos = 0) const;int find_first_of(const char *s, int pos, int n) const;int find_first_of(const string &s,int pos = 0) const;//从pos开始查找当前串中第一个在s的前n个字符组成的数组里的字符的位置。查找失败返回string::nposint find_first_not_of(char c, int pos = 0) const;int find_first_not_of(const char *s, int pos = 0) const;int find_first_not_of(const char *s, int pos,int n) const;int find_first_not_of(const string &s,int pos = 0) const;//从当前串中查找第一个不在串s中的字符出现的位置,失败返回string::nposint find_last_of(char c, int pos = npos) const;int find_last_of(const char *s, int pos = npos) const;int find_last_of(const char *s, int pos, int n = npos) const;int find_last_of(const string &s,int pos = npos) const;int find_last_not_of(char c, int pos = npos) const;int find_last_not_of(const char *s, int pos = npos) const;int find_last_not_of(const char *s, int pos, int n) const;int find_last_not_of(const string &s,int pos = npos) const;//find_last_of和find_last_not_of与find_first_of和find_first_not_of相似,只不过是从后向前查找 string类的替换函数12345678910string &replace(int p0, int n0, const char *s); // 删除从p0开始的n0个字符,然后在p0处插入串sstring &replace(int p0, int n0, const char *s, int n); // 删除p0开始的n0个字符,然后在p0处插入字符串s的前n个字符string &replace(int p0, int n0,const string &s);//删除从p0开始的n0个字符,然后在p0处插入串sstring &replace(int p0, int n0,const string &s, int pos, int n);//删除p0开始的n0个字符,然后在p0处插入串s中从pos开始的n个字符string &replace(int p0, int n0,int n, char c);//删除p0开始的n0个字符,然后在p0处插入n个字符cstring &replace(iterator first0, iterator last0,const char *s);//把[first0,last0)之间的部分替换为字符串sstring &replace(iterator first0, iterator last0,const char *s, int n);//把[first0,last0)之间的部分替换为s的前n个字符string &replace(iterator first0, iterator last0,const string &s);//把[first0,last0)之间的部分替换为串sstring &replace(iterator first0, iterator last0,int n, char c);//把[first0,last0)之间的部分替换为n个字符cstring &replace(iterator first0, iterator last0,const_iterator first, const_iterator last);//把[first0,last0)之间的部分替换成[first,last)之间的字符串 string类的插入函数123456789string &insert(int p0, const char *s);string &insert(int p0, const char *s, int n);string &insert(int p0,const string &s);string &insert(int p0,const string &s, int pos, int n);//前4个函数在p0位置插入字符串s中pos开始的前n个字符string &insert(int p0, int n, char c);//此函数在p0处插入n个字符citerator insert(iterator it, char c);//在it处插入字符c,返回插入后迭代器的位置void insert(iterator it, const_iterator first, const_iterator last);//在it处插入[first,last)之间的字符void insert(iterator it, int n, char c);//在it处插入n个字符c string类的删除函数123iterator erase(iterator first, iterator last);//删除[first,last)之间的所有字符,返回删除后迭代器的位置iterator erase(iterator it);//删除it指向的字符,返回删除后迭代器的位置string &erase(int pos = 0, int n = npos);//删除pos开始的n个字符,返回修改后的字符串]]></content>
</entry>
<entry>
<title><![CDATA[CSS实现滚动的球]]></title>
<url>%2F2017%2F09%2F04%2FCSS3%2F%E6%BB%9A%E5%8A%A8%E7%9A%84%E7%90%83%2F</url>
<content type="text"><![CDATA[1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><style> .wrapper{ border: 1px solid black; width: 300px; height: 300px; border-radius: 50%; margin: 90px auto; /* animation: ballRotate 2s linear 0s infinite; */ perspective: 3000px; transform-style: preserve-3d; position: relative; transform: rotateX(-30deg) rotateZ(-30deg); background: chocolate; } .wrapper::after{ content: ''; position: absolute; width: 2px; height: 500px; left: 149px; background: #a94c4c; top: -100px; border-radius: 5px; } .inner1{ width: 300px; height: 300px; border: 1px solid red; border-radius: 50%; transform: rotateY(36deg); position: absolute; top: 0; left: 0; background: antiquewhite; } .f1{ border: 1px solid blue; transform: rotateY(72deg); background: aqua; } .f2{ border: 1px solid green; transform: rotateY(108deg); background: brown; } .f3{ border: 1px solid #464646; transform: rotateY(144deg); background: blueviolet; } @keyframes ballRotate{ from { transform: rotateX(-30deg) rotateZ(-30deg) rotateY(0); } to{ transform: rotateX(-30deg) rotateZ(-30deg) rotateY(360deg); } }</style><body> <div class="wrapper"> <div class="inner1"></div> <div class="inner1 f1"></div> <div class="inner1 f2"></div> <div class="inner1 f3"></div> </div></body></html>]]></content>
</entry>
<entry>
<title><![CDATA[CSS中的单位]]></title>
<url>%2F2017%2F08%2F26%2FCSS3%2FCSS%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BD%8D%2F</url>
<content type="text"><![CDATA[CSS中的单位 emem被定义为相对于当前对象内文本的字体大小。如果你给body元素设置了一个字体大小,那么body的任何子元素的em值都等于body设置的font-size。 123456789<body> <div class="test">Test</div></body>body { font-size: 14px;}div { font-size: 1.2em; // calculated at 14px * 1.2, or 16.8px} div中的字体大小是1.2em,也就是div从父类元素继承的字体大小的1.2倍。在这里,body的字体是14px,那么div的字体大小是1.2*14 = 16.8px 但是,如果你用em一层一层级联地定义嵌套元素的字体大小又会发生什么事情呢?1234567891011<body> <div> Test <!-- 14 * 1.2 = 16.8px --> <div> Test <!-- 16.8 * 1.2 = 20.16px --> <div> Test <!-- 20.16 * 1.2 = 24.192px --> </div> </div> </div></body> 虽然在某些地方这正是我们想要的,但是通常情况下我们还是希望就依赖单一的相对度量单位就好。这时,就因该用rem了,rem中的r代表根元素,它的值就是根元素设置的字体大小。在大多数情况下根元素就是html了。 123456html { font-size: 14px;}div { font-size: 1.2rem;} 这样在上面的那三个嵌套的div的字体大小都是1.2 * 14px = 16.8px了。 适用于网格布局rem不仅适用于字体大小,也用于网格布局。例如,你可以用基于HTML根元素字体大小的rem作为整个网络布局或者UI库的大小单位,然后在其它特定的地方用em单位。这样将会给你带来更多的字体大小和伸缩的可控性。 123.container { width: 70rem; // 70 * 14px = 980px} vh和vw响应式web设计对百分比规则有很大的依赖性。然而,对于每一个问题,css百分比并不是最好的解决方案。CSS宽度是相对于包含它的最近的父元素的宽度的。如果你想使用的是视口的高度与宽度,而不是父元素的,那么vh和vw就能满足需求了。 1vh等于1%的视口高度。例如,浏览器高度是900px,那么1vh = 900* 1% = 9px,同理,若视口宽度是750px,则1vw是7.5px。它的用途很广泛。比如,我们用很简单的方法只用一行css代码就实现同屏幕等高的框。123.slide{ height: 100vh;} 假如你要来一个和屏幕同宽的标题,你只要设置这个标题的font-size的单位为vw,那标题的字体大小就会自动根据浏览器的宽度进行缩放,以达到字体和viewport大小同步的效果。 vmin和vmaxvh和vw是相对于视口的宽度和高度,而vmin和vmax则关于视口高度和宽度两者的最小或者最大值。例如,如果浏览器的高宽分别为700px和1100px,则1vmin=7px, 1vmax=11px;如果高宽分别是1080px和800px,则1vmin=8px,1vmax = 10.8px。 那么什么时候需要这些值呢?假设有一个元素,你需要让他始终在屏幕上可见。只要对其高度和宽度使用vmin单位,并赋予其低于100的值就可以做到。 1234.box { height: 100vmin; width: 100vmin;} 如果你要让这个方框始终铺满整个视口的可见区域(四边始终触摸到屏幕的四边): 1234.box{ height: 100vmax; width: 100vmax;}]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript工作机制(二)]]></title>
<url>%2F2017%2F08%2F26%2FJavaScript%2FJavaScript%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B62%2F</url>
<content type="text"><![CDATA[JavaScript工作机制(二) 概述JavaScript引擎是一个执行JavaScript代码的程序或解释器。JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器。 下面是实现了JavaScript引擎的一个热门项目列表: V8 — 开源,由Google开发,用C++编写的Rhino — 由Mozilla基金所管理,开源,完全用Java开发SpiderMonkey —第一个JavaScript引擎,最早用在Netscape Navigator上,现在用在Firefox上。JavaScriptCore — 开源,以Nitro销售,由苹果公司为Safari开发KJS —KDE的引擎最初由Harri Porten开发,用于KDE项目的Konqueror浏览器Chakra (JScript9) — Internet ExplorerChakra (JavaScript) — Microsoft EdgeNashorn— 开源为OpenJDK的一部分,由Oracle的Java语言和工具组开发JerryScript — 是用于物联网的轻量级引擎 创建V8引擎的由来Google构建的V8引擎是开源的,是用C++编写的。该引擎被用在Google Chrome中。不过,与其它引擎不同的是,V8还被用作Node.js的运行时。V8最初是设计用来提升Web浏览器中JavaScript执行的性能。为了获得速度,V8将JavaScript代码转化为更高效的机器码,而不是使用解释器。它通过实现像很多现代JavaScript引擎(spiderMonkey或Rhino)所用的JIT编译器,从而将JavaScript代码编译成机器码。这里主要区别在于V8不会产生字节码或任何中间代码 V8曾经有两个编译器在V8的5.9版出现之前,V8引擎用了两个编译器: full-codegen:一个简单而超快的编译器,可以生成简单而相对较慢的机器码 Crankshaft:一个更复杂(即时)的优化的编译器,可以生成高度优化的代码 V8引擎还在内部使用多个线程: 主线程执行我们想让他干的活:获取代码,编译然后执行它 还有一个单独的线程用于编译,这样在主线程继续执行的同时,单独的线程能同时在优化代码 一个Profiler线程,用于让运行时知道哪些方法花了大量时间,这样Crankshaft就可以对它进行优化 几个线程用于处理垃圾收集器扫描 第一次执行JavaScript代码时,V8会利用full-codegen直接将解析的JavaScript翻译为机器码,而无需任何转换。这就让它能非常快的开始执行机器码。请注意,由于V8不会使用中间字节码表示,这就无需解释器。 代码运行了一段时间后,Profiler线程已经收集了足够多的数据来判断应该优化哪个方法。 接下来,Crankshaft优化从另一个线程开始。它将JavaScript抽象语法树翻译为称为Hydrogen的高级静态单赋值(SSA)表示,并尝试优化Hydrogen图。大多数优化都在这一级完成的。 内联第一个优化是提前内联尽可能多的代码。内联是被调用的函数体替换调用位置(调用函数所在的代码行)的过程。这个简单的步骤让以下优化变得更有意义。 隐藏类JavaScript是一种基于原型的语言:它没有类,对象是用一种克隆过程创建的。JavaScript也是一种动态编程语言,就是说在对象实例化之后,可以随意给对象添加或删除属性。 大多数JavaScript解释器都使用类似字典的结构(基于哈希函数),将对象属性值的位置存储在内存中。这种结构使得在JavaScript中获取属性的值比在Java或C#这样的非动态编程语言中更昂贵。在Java中,所有对象属性都是由编译前的固定对象布局确定的,并且不能在运行时动态添加或删除。因此,属性的值(或指向这些属性的指针)可以在内存中存为连续缓冲区,每个缓冲区之间有固定偏移量。偏移量的长度可以很容易根据属性类型来确定。而在JavaScript中,这是不可能的,因为属性类型可能会在运行期间发生变化。 由于用字典来查找内存中对象属性的位置是非常低效的,所以V8使用了不同的方法来替代:隐藏类。隐藏类的工作机制类似于像Java这样的语言中使用的固定对象布局(类),只不过隐藏类是在运行时创建的。 例子:12345function Point(x, y) { this.x = x; this.y = y;}var p1 = new Point(1, 2) 一旦new Point(1, 2)调用发生了,V8就会创建一个称为C0的隐藏类。 因为还没有给Point定义属性,所以CO为空。 一旦执行了第一条语句this.x = x (在Point函数中),V8就会创建一个基于C0的第二个隐藏类C1。C1描述了内存中的位置(相对于对象指针),属性X在这个位置可以找到。此时,x存储在偏移0处,就是说,当将内存中的point对象作为连续缓存器来查看时,第一个偏移就对应属性x。V8也会用“类转换”来更新C0,指出如果将一个属性X添加到点对象,那么隐藏类应该从C0切换到C1。下面的point对象的隐藏类现在是C1 每当向对象添加一个新属性时,旧的隐藏类就被用一个转换路径更新为新的隐藏类。隐藏类转换很重要,因为它们可以让隐藏类在以相同方式创建的对象之间共享。如果两个对象共享一个隐藏类,并且将相同的属性添加到这两个对象中,那么转换会确保两个对象都收到相同的新隐藏类和它附带的所以优化过的代码。 当执行语句this.y = y时,会重复此过程。这时,又创建一个名为C2的新隐藏类,类转换被添加到C1,表示如果将属性y添加到Point对象(已包含属性x),那么隐藏类应更改为C2,同时point对象的隐藏类被更新为C2。 隐藏类转化取决于将属性添加到对象的顺序。如下: 1234567891011function Point (x, y) { this.x = x; this.y = y;}var p1 = new Point(1, 2);p1.a = 5;p1.b = 6;var p2 = new Point(3, 4);p2.a = 7;p2.b = 8; 现在,你可能会认为p1和p2会使用相同的隐藏类和转换。但这是错的。对于p1,首先是添加属性a,然后是属性b。不过,对于p2,先是给b赋值,然后才是a。因此,由于转换路径不同,p1和p2最终会有不同的隐藏类。在这种情况下,以相同的顺序初始化动态属性要更好,这样隐藏类才可以被重用。 内联缓存V8利用另一种称为内联缓存的技术来优化动态类型语言。内联缓存来自于观察的结果:对同一方法的重复调用往往发生在同一类型的对象上。 下面我们打算谈谈内联缓存的一般概念: V8维护在最近的方法调用中作为参数传递的对象类型的缓存,并使用该信息对将来作为参数传递的对象类型做出假设。如果V8能够对传递给方法的对象类型做出一个很好的假设,那么它可以绕过算出如何访问对象的属性的过程,转而使用先前查找对象的隐藏类时所存储的信息。 那么隐藏类和内联缓存的概念是如何关联的呢?无论何时在特定对象上调用方法,V8引擎必须对该对象的隐藏类执行查找,以确定访问特定属性的偏移量。在对同一个隐藏类的同一方法进行了两次成功的调用之后,V8就省略掉了隐藏类的查找,只将属性的偏移量添加到对象指针本身上。对于所以将来对该方法的调用,V8引擎都会假设隐藏类没有改变,并使用先前查找中存储的偏移量直接转到特定属性的内存地址。这会大大提高执行速度。 内联缓存也是为什么同一类型的对象共享隐藏类非常重要的原因。如果您创建相同类型的两个对象,但是用的是不同的隐藏类,那么V8将无法使用内联缓存,因为即使两个对象的类型相同,但是它们的对应隐藏类也会为其属性分配不同的偏移量。 编译到机器码一旦Hydrogen图被优化,Crankshaft将其降低到一个称为Lithium的较低级别表示。大多数Lithium实现都是针对架构的。寄存器分配发生在这一级。 最后,Lithium被编译成机器码。然后其它事情,也就是OSR发生了。在我们开始编译和优化一个明显要长期运行的方法之前,我们可能会运行它。V8不会蠢到忘记它刚刚慢慢执行的代码,所以它不会再用优化版本又执行一遍,而是将转换所有已有的上下文(栈,寄存器),以便我们可以在执行过程中间就切换到优化版本。这是一个非常复杂的任务,请记住,除了其它优化之外,V8最开始时已经内联了代码。V8并非唯一能做到这一点的引擎。有一种称为去优化的保护措施,会做出相反的转换,并恢复为非优化的代码,以防止引擎的假设不在成立。 垃圾回收对于垃圾回收来说,V8采用的是标记,清扫这种传统方式来清除旧一代。标记阶段应该停止执行JavaScript。为了控制GC成本,并使执行更加稳定,V8使用增量式标记:不是遍历整个堆,尝试标记每一个可能的对象,而是只遍历一部分堆,然后恢复正常执行。下一个GC停止会从之前的堆遍历停止的地方继续。这就允许在正常执行期间有非常短的暂停。如前所述,清扫阶段是由单独的线程处理。 如何编写优化的JavaScript 对象属性的顺序:始终以相同的顺序实例化对象属性,以便可以共享隐藏类和随后的优化的代码。 动态属性:在实例化后向对象添加属性会强制修改隐藏类,减慢为之前的隐藏类优化了的方法。所以应该在构造函数中指定对象的所以属性。 方法:重复执行相同方法的代码将比只执行一次的代码(由于内联缓存)运行的快。 数组:避免键不是增量数字的稀疏数组。元素不全的稀疏数组是一个哈希表,而访问这种数组中的元素更昂贵。另外,尽量避免预分配大数组。最好随着发展而增长。最后,不要删除数组中的元素。它会让键变得稀疏。 标记值:V8用32位表示对象和数字。它用一位来判断是对象(flag=1)还是整数(flag=0)。然后,如果一个数值大于31位,V8将会对数字装箱,将其转化为double,并创建为一个新对象将该数字放到里面。所以要尽可能使用31有符号数字,从而避免昂贵的转化为js对象的装箱操作。]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript工作机制(一)]]></title>
<url>%2F2017%2F08%2F26%2FJavaScript%2FJavaScript%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6%2F</url>
<content type="text"><![CDATA[JavaScript工作机制(一) 本文转载:原文链接:http://www.zcfy.cc/article/3965 概述几乎所有人都已经听说过V8引擎的概念,大多数人都知道JavaScript是单线程的,或者是使用回调队列。在这篇文章中,我们将详细介绍这些概念,并解释JavaScript的工作机制。通过了解这些细节,您将能正确利用提供的API,编写更好的非阻塞应用程序。 JavaScript引擎JavaScript引擎的一个流行示例是Google的V8引擎。例如,V8引擎在Chrome和Node.js中使用。如下所示: 引擎由两个主要部分组成: 内存堆:这是内存分配发生的地方 调用栈:代码执行所在的栈帧 运行时浏览器中已经有几个巨虎JavaScript开发人员都会使用的API(比如:setTimeout)。不过,这些API不是由引擎提供的。事实证明,现实有点复杂: 所以,除了引擎以外,实际上还有更多东西。有一些由浏览器提供的,称为Web API的东西,比如DOM,Ajax,setTimeout等等。还有事件循环和回调队列 调用栈JavaScript是一种单线程编程语言,这意味着他只有一个调用栈。因此,它一次只能做一个事。 调用栈是一种数据结构,它基本上记录了我们处于程序中哪个地方。如果单步执行一个函数,就把该函数放到栈顶如果从函数返回,就把它从栈顶弹出。这就是栈所做的事情。 并发和事件循环当在调用栈中有函数调用需要大量时间才能处理完时,会发生什么?例如,假如想在浏览器中使用JavaScript进行一些复杂的图像转换。 你可能会问-这怎么就成了一个问题呢?原因是,在调用堆有函数要执行的同时,浏览器实际上不能做任何事情–他被阻塞了。这意味着浏览器无法渲染,他不能运行任何其它代码,它只是卡住了。如果想在应用中有流畅的UI,则会出现问题。 而这不是唯一的问题。一旦浏览器开始处理调用栈中的许多任务,他可能会停止响应很长时间。 那么,如何执行繁重的代码,而不阻塞UI并且不会使浏览器无响应呢?解决的方案是异步回调]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript的深拷贝与浅拷贝]]></title>
<url>%2F2017%2F08%2F24%2FJavaScript%2FJavaScript%E9%87%8C%E7%9A%84%E6%B7%B1%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B5%85%E6%8B%B7%E8%B4%9D%2F</url>
<content type="text"><![CDATA[JavaScript的深拷贝与浅拷贝 对于字符串类型,浅复制是对值的复制,对于对象来说,浅复制是对对象地址的复制,没有开辟新的内存,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象也会改变,而深复制是开辟新的地址,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。 1234567891011121314var cloneObj = function (obj) { var str, newobj = obj.constructor === Array ? [] : {} if (typeof obj !== 'object') { return } else if (window.JSON) { str = JSON.stringify(obj) newobj = JSON.parse(str) } else { for (let i in obj) { newobj[i] = typeof obj[i] === 'object'? cloneObj(obj[i]) : obj[i] } } return newobj}]]></content>
</entry>
<entry>
<title><![CDATA[html重绘与重排]]></title>
<url>%2F2017%2F08%2F23%2FHTML5%2Fhtml%E9%87%8D%E7%BB%98%E4%B8%8E%E9%87%8D%E6%8E%92%2F</url>
<content type="text"><![CDATA[html重绘与重排 浏览器从下载文档到显示文档的过程是个复杂的过程,这里包含了重绘和重排。各家浏览器引擎的工作原理略有差别,但也有一定规则。文档初次加载时,浏览器引擎会解析HTML文档来构建DOM树,之后根据DOM元素的几何属性构建一颗用于渲染的树。渲染树的每个节点都有大小和边距属性,类似于盒子模型(由于隐藏元素不需要显示,渲染树并不包含隐藏的元素)。当渲染树构建完成后,浏览器就可以将元素放置到正确的位置,再根据渲染树节点的样式属性绘制出页面。由于浏览器的流布局,对渲染树的计算通常只需要一次遍历就可以完成。但table及其内部元素除外,他可能需要多次计算才能确定好其在渲染树的属性,通常要花3倍于同等元素时的时间。这也就是为什么我们需要避免使用table做布局的一个原因。 重绘是一个元素外观的改变所触发的浏览器行为,例如改变visibility,outline,背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现出新的外观。重绘不会带来重新布局,并不一定伴随重排。 重排是更明显的一种改变,可以理解为渲染树需要重新计算。下面是常见的触发重排的操作: DOM元素的几何属性变化当DOM元素的几何属性变化时,渲染树中的相关节点就会失效,浏览器会根据DOM元素的变化重新构建渲染树中失效的节点。之后,会根据新的渲染树重新绘制这部分页面。而且,当前元素的重排也会带来相关元素的重排。例如,容器节点的渲染树改变时,会触发子节点的重新计算,也会触发其后续兄弟节点的重排,祖先节点需要重新计算子节点的尺寸也会产生重排。最后,每个元素都将发生重绘。可见,重排一定会引起浏览器的重绘,一个元素的重排通常会带来一系列的反应,甚至触发整个文档的重排和重绘,性能代价是高昂的。 DOM树的结构变化当DOM树的结果变化时,例如节点的增减,移动等,也会触发重排。浏览器引擎布局的过程,类似于树的前序遍历,是一个从上到下从左到右的过程。通常在这个过程中,当前元素不会再影响其前面已经遍历过的元素。所以,如果在body最前面插入一个元素,会导致整个文档的重新渲染,而在其后插入一个元素,则不会影响到前面的元素。 获取某些属性 浏览器引擎可能会针对重排做了优化,比如Opera,它会等到有足够的数量变化发生,或者等到一定的时间,或者等一个线程结束,再一起处理,这样就只发生一次重排。但除了渲染树的直接变化,当获取一些属性时,浏览器为取得正确的值会触发重排。这样就使得浏览器的优化失效了。这些属性包括:offsetTop, offsetLeft, offsetWidth, offsetHeight, scrollTop, scrollLeft, scrollWidth, scrollHeight, clientTop, clientLeft, clientWidth, clientHeight, getComputedStyle() 。所以,在多次使用这些值时应进行缓存。 此外,改变元素的一些样式,调整浏览器窗口大小等等也将会触发重排。 开发中,比较好的实践是尽量减少重排次数和缩小重排的影响范围。例如: 1. 将多次改变样式属性的操作合并成一次操作。例如, 12345 JS:var changeDiv = document.getElementById(‘changeDiv’);changeDiv.style.color = ‘#093′;changeDiv.style.background = ‘#eee’;changeDiv.style.height = ’200px’; 可以合并为: 12345div.changeDiv { background: #eee; color: #093; height: 200px;} <code> document.getElementById(‘changeDiv’).className = ‘changeDiv’;</code> 2. 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。 3. 在内存中多次操作节点,完成后再添加到文档中去。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。 4. 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。 5. 在需要经常获取那些引起浏览器重排的属性值时,要缓存到变量。]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript中的top,clientTop,scrollTop,offsetTop的讲解]]></title>
<url>%2F2017%2F08%2F23%2FJavaScript%2FJavaScript%E7%9A%84top%E5%92%8CclientTop%E5%92%8CscrollTop%E5%92%8CoffsetTop%E7%9A%84%E8%AE%B2%E8%A7%A3%2F</url>
<content type="text"><![CDATA[JavaScript中的top,clientTop,scrollTop,offsetTop的讲解 scrollWidth:对象的实际内容的宽度,不包边线宽度,会随对象中内容超过可视区后而变大clientWidth: 对象内容的可视区的宽度,不包括滚动条等边线,会随对象显示大小的变化而变化offsetWidth: 对象整体的实际宽度,包含滚动条等边线,会随对象显示大小的变化而变化offsetHeight:clientHeight + 滚动条 + 边框clientHeight: 内容可视区域的高度,不包含滚动条 toppadding + bottompadding + heightscrollHeight: 元素padding加上元素内容的高度,这个高度与滚动条无关,是元素的实际高度:toppadding + bottompadding + 内容heightoffsetLeft:获取对象相对于版面或由offsetParent属性指定的父坐标的计算左侧位置offsetTop:获取对象相对于版面或由offsetTop属性指定的父坐标的计算顶端位置event.clientX:相对于文档的水平坐标event.clientY:相对于文档的垂直坐标event.offsetX:相对容器的水平坐标event.offsetY:相对容器的垂直坐标]]></content>
</entry>
<entry>
<title><![CDATA[getBoundingClientRect用法]]></title>
<url>%2F2017%2F08%2F23%2FJavaScript%2FgetBoundingClientRect%E7%94%A8%E6%B3%95%2F</url>
<content type="text"><![CDATA[getBoundingClientRect用法 getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top,right,bottom,left等属性。 语法:rectObject = object.getBoundingClientRect() 返回值类型:TextRectangle对象,每个矩形具有整数性质表示的矩形,以像素为单位。 rectObject.top:元素上边到视窗上边的距离 rectObject.right:元素右边到视窗左边的距离 rectObject.bottom:元素下边到视窗上边的距离 rectObject.left:元素左边到视窗左边的距离 width和height 12let rectWidth = rectObject.right - rectObject.left, rectHeight = rectObject.bottom - rectObject.top]]></content>
</entry>
<entry>
<title><![CDATA[CSS3的display:table]]></title>
<url>%2F2017%2F08%2F20%2FCSS3%2Fcss%E7%9A%84display%E4%B8%BAtable%2F</url>
<content type="text"><![CDATA[CSS3的display:table 两种类型的表格布局 HTML Table:HTML Table是指使用原生的table标签。 CSS Table:CSS Table是指用CSS属性模仿HTML 表格的模型。 123456789table { display: table }tr { display: table-row }thead { display: table-header-group }tbody { display: table-row-group }tfoot { display: table-footer-group }col { display: table-column }colgroup { display: table-column-group }td, th { display: table-cell }caption { display: table-caption } 显而易见HTML Table使用标签table,tr、td等标签,就是使用CSS Table的相关属性来实现的。 table:指定对象作为块元素级的表格。 inline-table:指定对象作为内联元素级的表格。 table-caption:指定对象作为表格标题。 table-cell:指定对象作为表格单元格。 table-row:指定对象作为表格行。 table-row-group:指定对象作为表格行组。 table-column:指定对象作为表格列。 table-column-group:指定对象作为表格列组显示。 table-header-group:指定对象作为表格标题组。 table-footer-group:指定对象作为表格脚注组。 动态垂直居中对齐 添加 Hello World Hello World .wrapper{ height: 300px; width: 100%; background: #105B63; display: table; position: relative; color: white; } .wrapper > button{ position: absolute; top: 20px; left: 20px; padding: 5px 10px; display: inline-block; background: #BD4932; outline: none; border: 2px solid #db9e36; color: #FFD34E; border-radius: 10px; box-shadow: 0 2px 3px rgba(0, 0, 0, .5); cursor: pointer; } .wrapper > button:active{ border-color: #FFFAD5; color: #FFFAD5; } .wrapper div.box{ display: table-cell; text-align: center; vertical-align: middle; } 动态水平居中对齐为了让元素动态水平居中对齐,可以设置元素为display: inline-block。然后在该元素的外面包裹层设置text-align: center。这里的文本对齐缺点是有副作用的。外层包裹层的所有子元素继承了text-align: center,造成潜在的覆盖。 动态水平居中对齐 使用display: table HomeAboutClientContact Us 使用 display: inline-block HomeAboutClientsContact Us #wrapper2{ width: 100%; text-align: center; color: #FFFAD5; background: #FFD34E; text-shadow: 0 1px 1px rgba(0, 0, 0, .2); } #wrapper2 .orange{ color: #BD4932; } #wrapper2 .nav-table{ display: table; margin: auto; } #wrapper2 .nav-inline-block{ display: inline-block; } #wrapper2 ul{ margin: 0; padding: 0; } #wrapper2 ul li{ float: left; background: #bd4932; list-style: none; } #wrapper2 ul li a{ display: block; padding: .5em 1em; color: #FFFAD5; } 响应式布局css Table布局可以让一个元素表现上不像一个表格。只要将元素的display属性从table-cell切换到block,我们就能够将元素堆叠起来,你可以根据屏幕的可视区域改变元素的堆叠的顺序。 #wrapper3{ width: 100%; display: table; min-height: 300px; } #wrapper3 .box{ display: table-cell; text-align: center; vertical-align:middle; line-height: 13em; } #wrapper3 .box1{ background: #BD4932; } #wrapper3 .box2{ background: #105B63; } #wrapper3 .box3{ background: #FFFAD5; } 动态高度的页脚贴在页面底部页脚动态贴在底部需要满足以下两个条件: 当主体的内容高度不超过可视区域高度的时候,页脚贴在页面底部。 当主体的内容高度超过可视区域高度的时候,页脚将按正常布局。 添加内容到主体区域添加内容到底部这里是主体区域这里是底部 #wrapper4{ width: 100%; height: 400px; background: #FFFAD5; display: table; color: #BD4932; position: relative; } #wrapper4 .maincontent{ height: 100%; text-align: center; } #wrapper4 button{ position: absolute; padding: 5px 10px; display: inline-block; background: #BD4932; outline: none; border: 2px solid #db9e36; color: #FFD34E; border-radius: 10px; box-shadow: 0 2px 3px rgba(0, 0, 0, .5); cursor: pointer; } #wrapper4 button:active{ border-color: #FFFAD5; color: #FFFAD5; } #wrapper4 .btn2{ top: 60px; } #wrapper4 .foot{ height: 1px; display: table-row; background: #105B63; color: #FFFAD5; } document.querySelector("#wrapper4 .btn1").addEventListener("click", function(){ var element = document.createElement("div"); element.innerHTML = "额外添加的行额外添加的行额外添加的行"; document.querySelector(".maincontent").appendChild(element); }); document.querySelector("#wrapper4 .btn2").addEventListener("click", function(){ var element = document.createElement("div"); element.innerHTML = "额外添加到底部的行额外添加到底部的行额外添加到底部的行"; document.querySelector(".foot").appendChild(element); }); 圣杯布局 具有中心内容主体和固定宽度的侧边栏 在源代码中,允许中心内容主体列出现在第一个节点位置 允许任何一列的高度最高 头部左边栏主体内容右侧栏页脚底部 #wrapper5{ width: 100%; height: 300px; background: #FFFAD5; display: table; text-align: center; position: relative; } #wrapper5 button{ position: absolute; padding: 5px 10px; display: inline-block; background: #BD4932; outline: none; border: 2px solid #db9e36; color: #FFD34E; border-radius: 10px; box-shadow: 0 2px 3px rgba(0, 0, 0, .5); cursor: pointer; } #wrapper5 .header{ display: table-row; height: 2px; background: #FFD34E; } #wrapper5 .maincontent{ display: table; height: 100%; width: 100%; } #wrapper5 .maincontent .box{ display: table-cell; } #wrapper5 .maincontent .sidebarr{ width: 100px; background: #BD4932; } #wrapper5 .foot{ display: table-row; height: 1px; background: #105B63; color: #FFFAD5; }]]></content>
</entry>
<entry>
<title><![CDATA[javascript控制伪元素的方法汇总]]></title>
<url>%2F2017%2F08%2F15%2FJavaScript%2FJavaScript%E6%8E%A7%E5%88%B6%E4%BC%AA%E5%85%83%E7%B4%A0%E7%9A%84%E6%96%B9%E6%B3%95%E6%B1%87%E6%80%BB%2F</url>
<content type="text"><![CDATA[javascript控制伪元素的方法汇总 为什么不能用JavaScript直接获取伪元素?比如::before和::after伪元素,用于在css渲染中向元素的头部或尾部插入内容,它们不受文档约束,也不影响文档本身,只影响最终样式。这些添加的内容不会出现在DOM中,仅仅是在css渲染层加入。事实上,伪元素可以被浏览器渲染,但本身并不是DOM元素。它不存在于文档中,所以JavaScript无法直接操作它。而Jquery的选择器都是基于DOM元素的,因此也并不能直接操作伪元素。 伪元素有哪些?伪元素有六个,分别为::after, ::before, ::first-line, ::first-letter, ::selection, ::backdrop。在各大网页中最常用的伪元素:::after, ::before。 获取伪元素的属性值获取伪元素的属性值可以使用window.getComputedStyle()方法,获取伪元素的css样式声明对象,然后利用getPropertyValue方法或直接使用键值访问都可以获取对应的属性值。 window.getComputedStyle(element, [,[pseudoElement]]) 参数如下: element:Object : 伪元素的所在DOM元素 pseudoElement:String :伪元素类型。可选值有:::after, ::before, ::first-line, ::selection, ::backdrop 例子1234567891011121314151617// CSS代码#myId:before { content: "hello world!"; display: block; width: 100px; height: 100px; background: red;}// HTML代码<div id="myId"></div>// JS代码var myIdElement = document.getElementById("myId");var beforeStyle = window.getComputedStyle(myIdElement, ":before");console.log(beforeStyle); // [CSSStyleDeclaration Object]console.log(beforeStyle.width); // 100pxconsole.log(beforeStyle.getPropertyValue("width")); // 100pxconsole.log(beforeStyle.content); // "hello world!" 备注: getPropertyValue()和直接使用键值访问,都可以访问CSSStyleDeclaration Object。它们两者的区别有: 对于float属性,如果使用键值访问,则不能直接使用getComputedStyle(ele, null).float,而应该使用cssFloat与styleFloat 直接使用键值访问,则属性的键需要使用驼峰写法,如style.backgroundColor 使用getPropertyValue()方法不必以驼峰书写形式,例如:style.getPropertyValue(‘border-top-color’) 伪元素默认是display: inline。如果没有定义display属性,即使在css中显示设置了width的属性值为固定的大小如“100px”,但是最后获取的width值仍为”quto“。这是因为行内元素不能自定义设置宽度。解决办法是给伪元素修改display属性为”block”,”inline-block”或其它。 更改伪元素的格式 更换class来实现伪元素属性的更改 1234567891011121314// CSS代码.red::before { content: "red"; color: red; }.green::before { content: "green"; color: green;}// HTML代码<div class="red">内容内容内容内容</div>// jQuery代码$(".red").removeClass('red').addClass('green'); 使用CSSStyleSheet的insertRule来为伪元素修改样式:document.styleSheets[0].addRule(‘.red::before’, ‘color: green’); //支持IE 在标签中插入的内部样式: 12345var style = document.createElement("style"); document.head.appendChild(style); sheet = style.sheet; sheet.addRule('.red::before','color: green'); // 兼容IE浏览器sheet.insertRule('.red::before { color: green }', 0); // 支持非IE的现代浏览器 修改伪元素的content的属性值 使用CSSStyleSheet的insertRule来为伪元素修改样式 1234var latestContent = "修改过的内容";var formerContent = window.getComputedStyle($('.red'), '::before').getPropertyValue('content'); document.styleSheets[0].addRule('.red::before','content: "' + latestContent + '"'); document.styleSheets[0].insertRule('.red::before { content: "' + latestContent + '" }', 0); 使用DOM元素的data-*属性来更改content的值 123456789// CSS代码.red::before {content: attr(data-attr);color: red;}// HTML代码<div class="red" data-attr="red">内容内容内容内容</div>// JacaScript代码$('.red').attr('data-attr', 'green'); ::before和::after伪元素的常用用法总结 添加字符串:使用引号包括一段字符串,将会向元素内容中添加字符串。a:after { content: “after content”; } 使用attr()方法,调用当前元素的属性的值 12a:after { content: attr(href); }a:after { content: attr(data-attr); } 使用url方法,引用多媒体文件:a::before { content: url(logo.png); } 清除浮动 12.clear-fix { *overflow: hidden; *zoom: 1; }.clear-fix:after { display: table; content: ""; width: 0; clear: both; }]]></content>
</entry>
<entry>
<title><![CDATA[什么是机器学习]]></title>
<url>%2F2017%2F08%2F15%2Fmachinelearnng%2F%E4%BB%80%E4%B9%88%E6%98%AF%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2F</url>
<content type="text"><![CDATA[什么是机器学习 什么是机器学习利用计算机从历史数据中找出规律,并把这些规律用到对未来不确定场景的决策 基础知识: 概率论 数理统计 机器学习的原动力 从历史数据中找出规律,把这些规律用到对未来自动做出决定 用数据代替expert 经济驱动,数据变现 机器学习的典型应用购物篮分析 关联规则用户细分精准营销 聚类信用卡欺诈 决策树互联网广告 ctr预估推荐系统 协同过滤自然语言处理 情感分析 实体识别 更多应用 语音识别 智慧机器人 个性化医疗 私人虚拟助理 情感分析 手势控制 人脸识别 视频内容自动识别 自动驾驶 机器实时翻译 机器学习算法分类 算法分类1: 有监督学习:分类算法,回归算法 无监督学习:聚类 半监督学习:强化学习 算法分类2: 分类与回归 聚类 标注 算法分类: 生成模型:属于哪一类的概率 判别模型:属于哪一类]]></content>
</entry>
<entry>
<title><![CDATA[CSS3线性渐变]]></title>
<url>%2F2017%2F08%2F13%2FCSS3%2FCSS3%E7%BA%BF%E6%80%A7%E6%B8%90%E5%8F%98%2F</url>
<content type="text"><![CDATA[CSS3线性渐变 渐变是从一种颜色逐渐蜕变到另一种颜色。线性渐变就是沿着一根轴线(水平,垂直或某个角度)改变颜色,从起点到终点颜色进行颜色渐变。 线性渐变创建线性渐变你需要指定渐变的轴线和沿轴线变化的多种颜色,颜色将按与轴线垂直的方向被绘制,多种颜色间将实现渐变平滑过渡。具体语法如下:1linear-gradient(gradient_line, color1, color2, ...); gradient_line指定了渐变线,后面跟随多种沿轴线变化的颜色 轴线轴线可以省略,这时,它使用默认值”to bottom”,指定轴线可以使用两种方式: 使用角度 “0 deg”表示垂直向上,然后按顺时针方向增加角度,“90deg”指向右边。 使用常量 to top: 向上 0deg to right: 向右 90deg to bottom: 向下 180deg to left: 向右 270deg 常量可以组合使用,如”to top left”就是到左上角。 线性渐变的例子 垂直渐变 css代码:background-image: linear-gradient(#cd6600, #0067cd) 你也可以指定轴线,下面的几种方式都可以达到和上面一样的效果 1234background: linear-gradient(to top, #0067cd, #cd6600);background: linear-gradient(to bottom, #cd6600, #0067cd);background: linear-gradient(180deg, #cd6600, #0067cd);background: linear-gradient(to bottom, #cd6600 0%, #0067cd 100%); 斜角渐变 css代码:background-image: linear-gradient(135deg, #cd6600, #0067cd) 也可以指定负角度,如下: background: linear-gradient(-45deg, #0067cd, #cd6600); 多色渐变 css代码:background-image: linear-gradient(#6600cd, #cd6600 20%, #00cd66) 多色斜角渐变 css代码:linear-gradient(to top right, #cd6600, white, #0067cd) 重复线性渐变重复线性渐变就是线性渐变的重复版本 repeating-linear-gradient ,当你定义好了你的线性渐变方式后,重复线性渐变会基于轴线不断的重复你的渐变方式,直到占满整个背景。使用重复线性渐变的关键是你需要定义好颜色节点,需要注意的是你定义的最后一种颜色将和第一种颜色相接在一起,处理不当将导致颜色的急剧变化。 重复线性渐变的例子css代码:background-image: repeating-linear-gradient(#cd6600, #0067cd 20%, #cd6600 40%) css代码:background-image: repeating-linear-gradient(90deg, #cd6600, #0067cd 20%, #cd6600 40%) css代码:background-image: repeating-linear-gradient(135deg, #cd6600, #0067cd 20%, #cd6600 40%) 颜色节点颜色节点是沿着渐变轴线被放置的点,定义格式如下: = [ | ] 首先指定颜色,然后指定位置,使用百分比值或者长度值表示。百分比值对应轴线长度的百分比,0%表示起始点,而100%表示结束点;长度值指从轴线的起始点向结束点方向计算的数值。颜色节点通常放置在起始点和结束点之间,但不是必须的,轴线可以在两个方向上无限扩展,而颜色节点可以放置在线上的任何位置 。在每个颜色节点,线呈现为颜色节点的颜色。在两个颜色节点之间,线呈现为从一种颜色过渡到另一种颜色过度过程。在第一个颜色节点之前,线呈现为第一个颜色节点的颜色,而在最后一个节点,线呈现为最后一个颜色节点的颜色。 以下步骤定义了处理颜色节点列表的行为,应用这些规则后,所有颜色节点都将有一个明确的位置 如果第一个颜色节点没有指定位置,设置它的位置为0%,如果最后一个节点没有指定位置,设置其为100% 如果一个颜色节点的位置小于了在它之前的任意一个颜色节点的位置,设置其位置等于它之前所有的颜色节点位置的最大的位置 如果存在一些颜色节点没有指定位置,那么,为那些相邻的没有指定颜色节点,设置它们的位置使他们平均占据空间。 如果多个颜色节点有相同的位置,它们产生一个从一个颜色到另一个颜色的急剧的转换。从效果来看,就是从一种颜色突然改变到另一种颜色。 例子: css代码: background-image: linear-gradient(red, white 20%, blue) 相当于 background-image: linear-gradient(red 0%, white 20%, blue 100%) css代码: background-image: linear-gradient(red 40%, white, black, blue) 相当于 background-image: linear-gradient(red 40%, white 60%, black 80%, blue 100%) css代码: background-image: linear-gradient(red -50%, white, blue) 相当于 background-image: linear-gradient(red -50%, white 25%, blue 100%) css代码: background-image: linear-gradient(red 80px, white 0px, black, blue 100px) 相当于 background-image: linear-gradient(red 80px, white 80px, black 90px, blue 100px) 条纹背景在讲解颜色节点时,我们提到“如果多个颜色节点有相同的位置,它们产生一个从一种颜色到另一种颜色的急剧的转换。从效果来看,就是从一种颜色突然改变到另一种颜色。”根据这个定义,我们只需要将多个颜色节点设置到同一个位置,就可以轻易的得到条纹背景效果。 水平条纹背景将两种颜色的颜色节点位置设置成一样就可以产生条纹背景,如下: css代码: background-image: linear-gradient(#cd6600 50%, #0067cd 50%) 利用背景的重复机制,我们可以创造出更多的条纹 css代码: background-image: linear-gradient(#cd6600 50%, #0067cd 50%); background-size: 100% 20%; 这样我们就把整个背景划分为了10个条纹,但每个条纹的高度并不一定要一样,只要改变比例就可以做到: css代码: background-image: linear-gradient(#cd6600 80%, #0067cd 80%); background-size: 100% 20%; css代码: background-image: linear-gradient(#cd6600 33.3%, #0067cd 0, #0067cd 66.7%, #00cd66 0); background-size: 100% 20%; 或者创建分割线效果: css代码: background-image: linear-gradient(rgba(0, 0, 0, .5) 1px , #fff 1px); background-size: 100% 3px; 垂直条纹背景垂直条纹背景类似,只是需要转化一下宽和高的设置方式 css代码: background-image: linear-gradient(to right, #cd6600 50%, #0067cd 0); background-size: 20% 100%; 对角条纹背景 css代码: background-image: repeating-linear-gradient(60deg, #cd6600, #0067cd 10%, #0067cd 0, #0067cd 20%); 可以指定多种颜色: css代码: background-image: repeating-linear-gradient(60deg, #cd6600, #cd6600 10%, #0067cd 0, #0067cd 0, #0067cd 20%, #00cd66 0, #00cd66 30%); css代码: background-image: repeating-linear-gradient(60deg, #cd6600, #cd6600 10%, #0067cd 0, #0067cd 0, #0067cd 20%, #00cd66 0, #00cd66 30%);]]></content>
</entry>
<entry>
<title><![CDATA[CSS实现单行,多行文本溢出显示省略号]]></title>
<url>%2F2017%2F08%2F13%2FCSS3%2Fcss%E6%8E%A7%E5%88%B6%E6%96%87%E5%AD%97%2F</url>
<content type="text"><![CDATA[CSS实现单行,多行文本溢出显示省略号 实现单行文本的溢出显示省略号使用text-overflow: ellipsis,当然还需要加宽度width属性来兼容部分浏览。 实现方法: 123overflow: hidden;text-overflow: ellipsis;white-space: nowrap; 但是这个属性只支持单行文本的溢出显示省略号。多行文本溢出显示省略号方法如下:实现方法:1234display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;overflow: hidden; 适用范围:因为使用了webkit的css扩展属性,该方法适用于webkit浏览器及移动端; 注: -webkit-line-clamp: 用来限制在一个块元素显示的文本的行数。为了实现该效果,他需要组合其它的webkit属性。常用结合属性; display: -webkit-box: 必须结合的属性,将对象作为弹性伸缩盒子模型 -webkit-box-orient: 必须结合的属性,设置或检索伸缩盒对象的子元素的排列方式 1234567p{position: relative; line-height: 20px; max-height: 40px;overflow: hidden;}p::after{content: "..."; position: absolute; bottom: 0; right: 0; padding-left: 40px;background: -webkit-linear-gradient(left, transparent, #fff 55%);background: -o-linear-gradient(right, transparent, #fff 55%);background: -moz-linear-gradient(right, transparent, #fff 55%);background: linear-gradient(to right, transparent, #fff 55%);} 注: 将height设置为line-height的整数倍,防止超出的文字露出 给p::after添加渐变背景可避免文字只显示一半。 由于ie6-7不显示content内容,所以要添加标签兼容ie6-7(如:);兼容ie8需要将::after替换成:afetr。]]></content>
</entry>
<entry>
<title><![CDATA[STL的map常用操作]]></title>
<url>%2F2017%2F08%2F12%2Fc%E4%B8%8Ec%2B%2B%2FSTL%E7%9A%84map%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[STL的map常用操作 map中的构造函数 123456789map(); // 默认构造函数map(const map& m) // 拷贝构造函数map(iterator begin, iterator end ); //区间构造函数map(iterator begin, iterator end, const traits& _compare) //带比较谓词的构造函数map(iterator begin, iterator end, const traits& _compare, const allocator& all) //带分配器 数据的插入 12345678910111213141516#include <map>#include <string>#include <iostream>using namespace std;int main(){ map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, "student_one")); mapStudent.insert(pair<int, string>(2, "student_two")); mapStudent.insert(pair<int, string>(3, "student_three")); map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { cout<<iter->first<<” ”<<iter->second<<end; }} 第二种插入value_type数据12345678910111213141516#include <map>#include <string>#include <iostream>using namespace std;int main(){ Map<int, string> mapStudent; mapStudent.insert(map<int, string>::value_type (1, "student_one")); mapStudent.insert(map<int, string>::value_type (2, "student_two")); mapStudent.insert(map<int, string>::value_type (3, "student_three")); map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { cout<<iter->first<<” ”<<iter->second<<end; }} 第三种:用数组方式插入数据,下面举例说明12345678910111213141516#include <map>#include <string>#include <iostream>using namespace std;int main(){ Map<int, string> mapStudent; mapStudent[1] = “student_one”; mapStudent[2] = “student_two”; mapStudent[3] = “student_three”; map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { cout<<iter->first<<” ”<<iter->second<<end; }} 我们可以用pair来获得是否插入成功:1234567891011121314151617181920212223242526272829303132#include <map>#include <string>#include <iostream>Using namespace std;Int main(){ Map<int, string> mapStudent; Pair<map<int, string>::iterator, bool> Insert_Pair; Insert_Pair=mapStudent.insert(pair<int, string>(1, “student_one”)); If(Insert_Pair.second == true) { Cout<<”Insert Successfully”<<endl; } Else { Cout<<”Insert Failure”<<endl; } Insert_Pair=mapStudent.insert(pair<int, string>(1, “student_two”)); If(Insert_Pair.second == true) { Cout<<”Insert Successfully”<<endl; } Else { Cout<<”Insert Failure”<<endl; } map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){ Cout<<iter->first<<” ”<<iter->second<<end;}} 数据的查找(包括判定这个关键字是否在map中出现)在这里我们将体会,map在数据插入时保证有序的好处。要判定一个数据(关键字)是否在map中出现的方法比较多,这里标题虽然是数据的查找,在这里将穿插着大量的map基本用法。这里给出两种数据查找方法第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了第二种:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器,程序说明 123456789101112131415161718192021#include <map>#include <string>#include <iostream>using namespace std;int main(){ map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, “student_one”)); mapStudent.insert(pair<int, string>(2, “student_two”)); mapStudent.insert(pair<int, string>(3, “student_three”)); map<int, string>::iterator iter; iter = mapStudent.find(1); if(iter != mapStudent.end()) { Cout<<”Find, the value is ”<<iter->second<<endl; } else { Cout<<”Do not Find”<<endl; } } 数据的删除 这里要用到erase函数,它有三个重载了的函数,下面在例子中详细说明它们的用法1234567891011121314151617181920212223#include <map>#include <string>#include <iostream>Using namespace std;Int main(){ Map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, “student_one”)); mapStudent.insert(pair<int, string>(2, “student_two”)); mapStudent.insert(pair<int, string>(3, “student_three”));//如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好 //如果要删除1,用迭代器删除 map<int, string>::iterator iter; iter = mapStudent.find(1); mapStudent.erase(iter); //如果要删除1,用关键字删除 Int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0 //用迭代器,成片的删除 //一下代码把整个map清空 mapStudent.earse(mapStudent.begin(), mapStudent.end()); //成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合 //自个加上遍历代码,打印输出吧} 排序这里要讲的是一点比较高深的用法了,排序问题,STL中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是int型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert等函数在编译的时候过不去。第一种:小于号重载,程序举例12345678910111213141516171819202122232425262728293031#include <map>#include <string>Using namespace std;Typedef struct tagStudentInfo{ Int nID; String strName; Bool operator < (tagStudentInfo const& _A) const { //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序 If(nID < _A.nID) return true; If(nID == _A.nID) return strName.compare(_A.strName) < 0; Return false; }}StudentInfo, *PStudentInfo; //学生信息Int main(){ int nSize; //用学生信息映射分数 map<StudentInfo, int>mapStudent; map<StudentInfo, int>::iterator iter; StudentInfo studentInfo; studentInfo.nID = 1; studentInfo.strName = “student_one”; mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90)); studentInfo.nID = 2; studentInfo.strName = “student_two”;mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++) cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl;}]]></content>
</entry>
<entry>
<title><![CDATA[STL的vector初始化]]></title>
<url>%2F2017%2F08%2F12%2Fc%E4%B8%8Ec%2B%2B%2FSTL%E7%9A%84vector%E5%88%9D%E5%A7%8B%E5%8C%96%2F</url>
<content type="text"><![CDATA[STL的vector初始化 vector是连续内存容器,所以对于插入与删除的时间复杂度是很高的,因为删除或者插入的时候,需要元素的移动,即元素复制拷贝。 使用原则: 尽量使用vector代替C风格的数组或者CArray; 尽量使用算法代替手工写的循环; 尽量使用vector本身的函数代替其它泛型算法; 初始化vector填充vector,如果我们想使用原始数组的内容填充vector,例如我们有数组int v[10] = {0,1,1,10,0,0,0,0,3,5} 初始化方式1: 1234vector<int> v2(10); // 初始化size为10可以避免数组动态增长的时候不断的分配内存for(int i = 0; i<10; i++) { v2.push_back(v2[i]); // 增加一个元素} 初始化方式2: 1vector<int> v3(&v[0], &v[9]); // 原始数组的元素指针可以作为迭代器来使用 初始化方式3 123vector<int> v4;v4.resize(10);v4.insert(v4.begin(), &v[0], &v[9]); 初始化方式4 12vector<int> v5(10);copy(&v[0], &v[9], v5.begin()); 原始数组的元素指针可以作为迭代器来使用, 原则:尽量使用resize来减少不必要的内存分配次数。 原则:尽量使用empty而不是size()==0来判断容器是否为空 12345678910111213141516int a[] = {23,23,12,23,546,76};vector<int> v;v.resize(6);copy(&a[0], &a[6], v.begin()); // 将a数组复制到vv.insert(v.begin(), 4);// 找到所有的23vector<int>::iterator pos = find(v.begin(), v.end(), 23);while(pos!=v.end()){ cout<<"**"<<*pos<<endl; pos = find(pos+1, v.end(), 23);}for(int i = 0;i< v.size();i++){ cout<< v[i]<<" ";}return 0;]]></content>
</entry>
<entry>
<title><![CDATA[Vuex讲解]]></title>
<url>%2F2017%2F08%2F10%2FVue%2FVuex%E8%AE%B2%E8%A7%A3%2F</url>
<content type="text"><![CDATA[Vuex讲解 Vuex是什么?Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所以组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 状态管理模式12345678910111213141516new Vue({ //state data () { return { count: 0 } }, //view template: `<div>{{count}}</div>`, //actions methods: { increment () { this.count++ } }}) 这个状态自管理应用包括以下几个部分: state:驱动应用的数据源 view:以声明方式将state映射到视图 actions:响应在view上的用户输入导致的状态变化。 但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏: 多个视图依赖于同一状态 来自不同视图的行为需要变更同一状态 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致代码无法维护。 我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。 State单一状态树Vuex使用 单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个《《唯一数据源》》而存在。这也意味着,每个应用将仅仅包含一个store实例。单一状态树让我们能够直接的定位任意特定的状态片段,在调试的过程中也能够获得当前应用状态的快照。 在Vue组件中获得Vuex状态Vuex通过store选项,提供了一个机制将状态从根组件注入到每一个子组件中(需调用Vue.use(Vuex)):1234567891011const app = new Vue({ el: '#app', // 把store对象提供给“store”选项,这可以把store的实例注入到所有的子组件 store, component: {Counter}, template: ` <div class="app"> <counter></counter> </div> ` }) 通过在根实例中注册store选项,该store实例会注入到根组件的所以子组件中,且子组件能通过this.$store访问到。counter的实现如下:12345678const Counter = { template: `<div>{{counter}}</div>`, computed: { count () { return this.$store.state.count } }} mapState辅助函数当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。可以使用mapState辅助函数帮助我们生成计算属性1234567891011121314import {mapState} from 'vuex'export default{ //... computed: mapState({ // 箭头函数可使代码更简练 count: state => state.count, //传字符串参数 ‘count’等同于 `state => state.count` countAlias: 'count', //为了能够使用this获取局部状态,必须使用常规函数 countPlusLocalState (state) { return state.count + this.localCount } })} Getters有时候我们需要从store中的state中派生出一些状态,例如对列表进行过滤并计数:12345computed: { doneTodoCount () { return this.$store.state.todos.filter(todo => todo.done).length }} 如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它—无论哪种方式都不是很理想。Vuex允许我们在store中定义getter(可以认为是store的计算属性)。Getter接受state作为第一个参数:1234567891011121314const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '.....', done: true}, { id: 2, text: '.....', done: true}, { id: 3, text: '.....', done: false} ] }, getter: { doneTodos: state => { return state.todos.filter(todo => todo.done) } } }) Getter会暴露为store.getters对象:store.getters.doneTodos Getters也可以接受其它getters作为第二个参数:123456getters: { doneTodosCount: (state, getters) => { return getters.doneTodos.length }}store.getters.doneTodosCount 我们可以很容易地在任何组件中使用它:12345computed: { doneTodosCount () { return this.$store.getters.doneTodosCount }} mapGetters 辅助函数mapGetters 辅助函数仅仅是将store中的getters映射到局部计算属性: 12345678910import {mapGetters} from 'vuex'export default{ computed: { //使用对象展开运算符将getters混入computed对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter' ]) }} 如果你想将一个getter属性另取一个名字,使用对象形式: 1234...mapGetters({ // 映射 this.doneCount 为 store.getters.doneTodosCount doneCount: 'doneTodosCount' }) Mutations更改Vuex的store中的状态的唯一方法是提交mutation。Vuex中的mutations非常类似于事件:每个mutation都有一个字符串的事件类型和一个回调函数。这个回调函数就是我们实际进行状态更改的地方,并且它会接受state作为第一个参数: 1234567891011const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { //变更状态 state.count++ } }}) 不能直接调用一个mutation.handler。这个选项更像是事件注册:“当触发一个类型为increment的mutation时,调用此函数。”要唤醒一个mutation handler,你需要以相应的type调用store.commit方法。store.commit(‘increment’) 提交载荷你可以向store.commit传入额外的参数,即mutation的载荷:12345678// ...mutations: { increment (state, n){ state.count += n }}store.commit('increment', 10) 在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的mutation会更易读: 123456789mutation: { increment (state, payload) { state.count += payload.amount }}store.commit('increment', { amount: 10 }) Mutation需遵循Vue的响应规则既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项: 最好提前在你的 store 中初始化好所有所需属性。 当需要在对象上添加新属性时,你应该使用 Vue.set(obj, ‘newProp’, 123), 或者 -以新对象替换老对象。例如,利用 stage-3 的对象展开运算符我们可以这样写:state.obj = { …state.obj, newProp: 123 } ActionsAction类似于mutation,不同在于: Action提交的是mutation,而不是直接变更状态 Action可以包含任意异步操作 让我们来注册一个简单的action:123456789101112131415const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }}) Action 函数接受一个与store实例具有相同方法的属性的context对象,因此你可以调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state和getters。 实践中,我们会经常用到ES2015的参数解构来简化代码: 12345actions: { increment ({commit}) { commit('increment') }} 分发ActionAction通过store.dispatch方法触发:store.dispatch(‘increment’),mutation必须同步执行,但是我们可以在action内部执行异步操作 : 1234567actions: { incrementAsync ({commit}) { setTimeout(() => { commit('increment') }, 1000) }} Actions支持同样的载荷方式和对象方式进行分发: 12345678910// 以载荷形式分发store.dispatch('increment', { amount: 10 })//以对象形式分发store.dispatch({ type: 'incrementAsync', amount: 10 }) 在组件中分发Action你在组件中使用this.$store.dispatch(‘xxx’)分发action,或者使用mapActions辅助函数将组件的methods映射为store.dispatch调用(需要先在根节点注入store): 12345678910import { mapActions } from 'vuex'export default{ // ... methods: { ...mapActions([ 'increment' // 映射this.increment()为this.$store.dispatch('increment') add: 'increment' // 映射this.add() 为this.$store.dispatch('increment') ]) }} 组合ActionsAction通常是异步的,那么如何知道action什么时候结束呢?更重要的是,我们如何才能组合多个action,以处理更加复杂的异步流程? 首先,你需要明白store.dispatch可以处理被触发的action的回调函数返回的Promise,并且store.dispatch仍然返回Promise:12345678910actions: { actionA ({commit}) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) }} 现在你可以:123store.dispatch('actionA').then(() => { //... }) 在另外一个action中也可以:12345678actions: { // ... actionB ({dispatch, commit}) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) }} Modules由于使用单一状态树,应用的所以状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相对臃肿。 为了解决以上问题,Vuex允许我们将store分割成模块,每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块—从上至下进行同样方式分割: 12345678910111213141516171819202122const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... }}const moduleB = { state: { ... }, mutations: { ... }, actions: { ... }}const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB }})store.state.a // -> moduleA 的状态store.state.b // -> moduleB 的状态 模块的局部状态对于模块内部的mutation和getter,接受的第一个参数是模块的局部状态对象。123456789101112131415const moduleA = { state: { count: 0 }, mutations: { increment (state) { // 这里的 `state` 对象是模块的局部状态 state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } }} 同样,对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootState:12345678910const moduleA = { // ... actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } }} 对于模块内部的getter,根节点状态会作为第三个参数暴露出来:12345678const moduleA = { // ... getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } }} 命名空间默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。如果希望你的模块更加自包含或提高可重用性,你可以通过添加 namespaced: true 的方式使其成为命名空间模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如: 123456789101112131415161718192021222324252627282930313233343536const store = new Vuex.Store({ modules: { account: { namespaced: true, // 模块内容(module assets) state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响 getters: { isAdmin () { ... } // -> getters['account/isAdmin'] }, actions: { login () { ... } // -> dispatch('account/login') }, mutations: { login () { ... } // -> commit('account/login') }, // 嵌套模块 modules: { // 继承父模块的命名空间 myPage: { state: { ... }, getters: { profile () { ... } // -> getters['account/profile'] } }, // 进一步嵌套命名空间 posts: { namespaced: true, state: { ... }, getters: { popular () { ... } // -> getters['account/posts/popular'] } } } } }}) 启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。]]></content>
</entry>
<entry>
<title><![CDATA[类型化数组]]></title>
<url>%2F2017%2F08%2F07%2FJavaScript%2F%E7%B1%BB%E5%9E%8B%E5%8C%96%E6%95%B0%E7%BB%84%2F</url>
<content type="text"><![CDATA[类型化数组 类型化数组是JavaScript操作二进制数据的一个接口类型化数组是建立在ArrayBuffer对象的基础上的。它的作用是,分配一段可以存放数据的连续内存区域。var buf = new ArrayBuffer(32),这段代码生成了一段32字节的内存区域。ArrayBuffer对象的byteLength属性,返回所分配的区域区域的字节长度。var buffer = new ArrayBuffer(32); buffer.byteLength,如果分配的内存区域很大,有可能分配失败,所以有必要检查是否分配成功。12345if(buffer.byteLength===n){ //success}else{ //fail} ArrayBuffer对象有一个slice方法,允许将内存区域的一部分拷贝生成一个新的ArrayBuffer对象。12var buffer = new ArrayBuffer(8);var newBuffer = buffer.slice(0, 3); 上面代码拷贝buffer对象的前3个字节,生成一个新的ArrayBuffer对象。slice方法包含两步:第一步先分配一段新内存,第二部是将原来那个ArrayBuffer对象拷贝过去。slice方法接受两个参数,第一个参数表示拷贝开始的字节序号,第二个参数表示拷贝截至的字节序号。如果省略第二个参数,则默认到原ArrayBuffer对象的结尾。除了slice方法,ArrayBuffer对象不提供任何直接读取内存的方法,只允许再其上方建立视图,然后通过视图读写。 视图 视图的生成ArrayBuffer作为内存区域,可以存放多种类型的数据。不同数据有不同的存取方式,这就叫做”视图”。目前,JavaScript提供以下类型的视图: Int8Array:8位有符号整数,长度1个字节。 Uint8Array:8位无符号整数,长度1个字节。 Int16Array:16位有符号整数,长度2个字节。 Uint16Array:16位无符号整数,长度2个字节。 Int32Array:32位有符号整数,长度4个字节。 Uint32Array:32位无符号整数,长度4个字节。 Float32Array:32位浮点数,长度4个字节。 Float64Array:64位浮点数,长度8个字节。 每一种视图都有一个BYTE_PER_ELEMENT常数,表示这种数据类型占据的字节数。 Int8Array.BYTES_PER_ELEMENT // 1 Uint8Array.BYTES_PER_ELEMENT // 1 Int16Array.BYTES_PER_ELEMENT // 2 Uint16Array.BYTES_PER_ELEMENT // 2 Int32Array.BYTES_PER_ELEMENT // 4 Uint32Array.BYTES_PER_ELEMENT // 4 Float32Array.BYTES_PER_ELEMENT // 4 Float64Array.BYTES_PER_ELEMENT // 8 在ArrayBuffer对象之上生成视图同一个ArrayBuffer对象之上,可以根据不同的数据类型,建立多个视图。1234567891011// 创建一个8字节的ArrayBuffervar b = new ArrayBuffer(8);// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾var v1 = new Int32Array(b);// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾var v2 = new Uint8Array(b, 2);// 创建一个指向b的Int16视图,开始于字节2,长度为2var v3 = new Int16Array(b, 2, 2); 上面代码在一段长度为8个字节的内存之上,生成了三个视图:v1,v2,v3。视图的构造函数可以接受三个参数: 第一个参数:视图对应的底层ArrayBuffer对象,该参数是必需的。 第二个参数:视图开始的字节序号,默认从0开始。 第三个参数:视图包含的数据个数,默认直到本段内存区域结束。 因此,v1、v2和v3是重叠:v1[0]是一个32位整数,指向字节0~字节3;v2[0]是一个8位无符号整数,指向字节2;v3[0]是一个16位整数,指向字节2~字节3。只要任何一个视图对内存有所修改,就会在另外两个视图上反应出来。 直接生成视图还可以不通过ArrayBuffer对象,直接分配内存而生成。 1234var f64a = new Float64Array(8);f64a[0] = 10;f64a[1] = 20;f64a[2] = f64a[0] + f64a[1]; 上面代码生成一个8个成员的Float64Array数组(共64字节),然后依次对每个成员赋值。这时,视图构造函数的参数就是成员的个数。可以看到,视图数组的赋值操作与普通数组的操作毫无两样。 将普通数组转为视图数组将一个数据类型符合要求的普通数组,传入构造函数,也能直接生成视图。var typedArray = new Uint8Array( [ 1, 2, 3, 4 ] );,代码将一个普通的数组,赋值给一个新生成的8位无符号整数的视图数组。视图数组也可以转换回普通数组。var normalArray = Array.apply( [], typedArray ); 视图的操作建立了视图以后,就可以进行各种操作了。这里需要明确的是,视图其实就是普通数组,语法完全没有什么不同,只不过它直接针对内存进行操作,而且每个成员都有确定的数据类型。所以,视图就被叫做“类型化数组”。 数组操作普通数组的操作方法和属性,对类型化数组完全适用。 12345var buffer = new ArrayBuffer(16);var int32View = new Int32Array(buffer);for (var i=0; i<int32View.length; i++) { int32View[i] = i*2;} 上面代码生成一个16字节的ArrayBuffer对象,然后在它的基础上,建立了一个32位整数的视图。由于每个32位整数占据4个字节,所以一共可以写入4个整数,依次为0,2,4,6。如果在这段数据上接着建立一个16位整数的视图,则可以读出完全不一样的结果。 123456789101112var int16View = new Int16Array(buffer);for (var i=0; i<int16View.length; i++) { console.log("Entry " + i + ": " + int16View[i]);}// Entry 0: 0// Entry 1: 0// Entry 2: 2// Entry 3: 0// Entry 4: 4// Entry 5: 0// Entry 6: 6// Entry 7: 0 由于每个16位整数占据2个字节,所以整个ArrayBuffer对象现在分成8段。然后,由于x86体系的计算机都采用小端字节序(little endian),相对重要的字节排在后面的内存地址,相对不重要字节排在前面的内存地址,所以就得到了上面的结果。比如,一个占据四个字节的16进制数0x12345678,决定其大小的最重要的字节是“12”,最不重要的是“78”。小端字节序将最不重要的字节排在前面,储存顺序就是78563412;大端字节序则完全相反,将最重要的字节排在前面,储存顺序就是12345678。目前,所有个人电脑几乎都是小端字节序,所以类型化数组内部也采用小端字节序读写数据,或者更准确的说,按照本机操作系统设定的字节序读写数据。这并不意味大端字节序不重要,事实上,很多网络设备和特定的操作系统采用的是大端字节序。这就带来一个严重的问题:如果一段数据是大端字节序,类型化数组将无法正确解析,因为它只能处理小端字节序!为了解决这个问题,JavaScript引入DataView对象,可以设定字节序,下文会详细介绍。 下面是另一个例子。 1234567891011// 假定某段buffer包含如下字节 [0x02, 0x01, 0x03, 0x07]// 计算机采用小端字节序var uInt16View = new Uint16Array(buffer);// 比较运算 if (bufView[0]===258) { console.log("ok");}// 赋值运算uInt16View[0] = 255; // 字节变为[0xFF, 0x00, 0x03, 0x07]uInt16View[0] = 0xff05; // 字节变为[0x05, 0xFF, 0x03, 0x07]uInt16View[1] = 0x0210; // 字节变为[0x05, 0xFF, 0x10, 0x02] 总之,与普通数组相比,类型化数组的最大优点就是可以直接操作内存,不需要数据类型转换,所以速度快得多。 buffer属性类型化数组的buffer属性,返回整段内存区域对应的ArrayBuffer对象。该属性为只读属性。var a = new Float32Array(64);var b = new Uint8Array(a.buffer);上面代码的a对象和b对象,对应同一个ArrayBuffer对象,即同一段内存。 byteLength属性和byteOffset属性byteLength属性返回类型化数组占据的内存长度,单位为字节。byteOffset属性返回类型化数组从底层ArrayBuffer对象的哪个字节开始。这两个属性都是只读属性。12345678910111213var b = new ArrayBuffer(8);var v1 = new Int32Array(b);var v2 = new Uint8Array(b, 2);var v3 = new Int16Array(b, 2, 2);v1.byteLength // 8v2.byteLength // 6v3.byteLength // 4v1.byteOffset // 0v2.byteOffset // 2v3.byteOffset // 2 注意将byteLength属性和length属性区分,前者是字节长度,后者是成员长度。 123var a = new Int16Array(8);a.length // 8a.byteLength // 16 set方法类型化数组的set方法用于复制数组,也就是将一段内容完全复制到另一段内存。123var a = new Uint8Array(8);var b = new Uint8Array(8);b.set(a); 上面代码复制a数组的内容到b数组,它是整段内存的复制,比一个个拷贝成员的那种复制快得多。set方法还可以接受第二个参数,表示从b对象哪一个成员开始复制a对象。 123var a = new Uint16Array(8);var b = new Uint16Array(10);b.set(a,2) 上面代码的b数组比a数组多两个成员,所以从b[2]开始复制。 subarray方法subarray方法是对于类型化数组的一部分,再建立一个新的视图。 1234var a = new Uint16Array(8);var b = a.subarray(2,3);a.byteLength // 16b.byteLength subarray方法的第一个参数是起始的成员序号,第二个参数是结束的成员序号(不含该成员),如果省略则包含剩余的全部成员。所以,上面代码的a.subarray(2,3),意味着b只包含a[2]一个成员,字节长度为2。 ArrayBuffer与字符串的互相转换ArrayBuffer转为字符串,或者字符串转为ArrayBuffer,有一个前提,即字符串的编码方法是确定的。假定字符串采用UTF-16编码(JavaScript的内部编码方式),可以自己编写转换函数。1234567891011121314// ArrayBuffer转为字符串,参数为ArrayBuffer对象function ab2str(buf) { return String.fromCharCode.apply(null, new Uint16Array(buf));}// 字符串转为ArrayBuffer对象,参数为字符串function str2ab(str) { var buf = new ArrayBuffer(str.length*2); // 每个字符占用2个字节 var bufView = new Uint16Array(buf); for (var i=0, strLen=str.length; i<strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf;} DataView如果一段数据包括多种类型(比如服务器传来的HTTP数据),这时除了建立ArrayBuffer对象的复合视图以外,还可以通过DataView视图进行操作。DataView视图提供更多操作选项,而且支持设定字节序。本来,在设计目的上,ArrayBuffer对象的各种类型化视图,是用来向网卡、声卡之类的本机设备传送数据,所以使用本机的字节序就可以了;而DataView的设计目的,是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的。DataView本身也是构造函数,接受一个ArrayBuffer对象作为参数,生成视图。DataView(ArrayBuffer buffer [, 字节起始位置 [, 长度]]); getInt8:读取1个字节,返回一个8位整数。 getUint8:读取1个字节,返回一个无符号的8位整数。 getInt16:读取2个字节,返回一个16位整数。 getUint16:读取2个字节,返回一个无符号的16位整数。 getInt32:读取4个字节,返回一个32位整数。 getUint32:读取4个字节,返回一个无符号的32位整数。 getFloat32:读取4个字节,返回一个32位浮点数。 getFloat64:读取8个字节,返回一个64位浮点数。 这一系列get方法的参数都是一个字节序号,表示从哪个字节开始读取。1234567891011var buffer = new ArrayBuffer(24);var dv = new DataView(buffer);// 从第1个字节读取一个8位无符号整数var v1 = dv.getUint8(0);// 从第2个字节读取一个16位无符号整数var v2 = dv.getUint16(1); // 从第4个字节读取一个16位无符号整数var v3 = dv.getUint16(3); 上面代码读取了ArrayBuffer对象的前5个字节,其中有一个8位整数和两个十六位整数。如果一次读取两个或两个以上字节,就必须明确数据的存储方式,到底是小端字节序还是大端字节序。默认情况下,DataView的get方法使用大端字节序解读数据,如果需要使用小端字节序解读,必须在get方法的第二个参数指定true。 12345678// 小端字节序var v1 = dv.getUint16(1, true);// 大端字节序var v2 = dv.getUint16(3, false);// 大端字节序var v3 = dv.getUint16(3); DataView视图提供以下方法写入内存: setInt8:写入1个字节的8位整数。setUint8:写入1个字节的8位无符号整数。setInt16:写入2个字节的16位整数。setUint16:写入2个字节的16位无符号整数。setInt32:写入4个字节的32位整数。setUint32:写入4个字节的32位无符号整数。setFloat32:写入4个字节的32位浮点数。setFloat64:写入8个字节的64位浮点数。 这一系列set方法,接受两个参数,第一个参数是字节序号,表示从哪个字节开始写入,第二个参数为写入的数据。对于那些写入两个或两个以上字节的方法,需要指定第三个参数,false或者undefined表示使用大端字节序写入,true表示使用小端字节序写入。 12345678// 在第1个字节,以大端字节序写入值为25的32位整数dv.setInt32(0, 25, false); // 在第5个字节,以大端字节序写入值为25的32位整数dv.setInt32(4, 25); // 在第9个字节,以小端字节序写入值为2.5的32位浮点数dv.setFloat32(8, 2.5, true); 如果不确定正在使用的计算机的字节序,可以采用下面的判断方式。12345var littleEndian = (function() { var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256;})(); 如果返回true,就是小端字节序;如果返回false,就是大端字节序。]]></content>
</entry>
<entry>
<title><![CDATA[数据库安全性]]></title>
<url>%2F2017%2F08%2F07%2FDataBase%2F%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%89%E5%85%A8%E6%80%A7%2F</url>
<content type="text"><![CDATA[数据库安全性 数据库的特点之一是由数据库管理系统提供统一的数据保护功能来保护数据的安全可靠和正确有效。数据库的数据保护主要包括数据的安全性和完整性。 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露,更改或破坏 数据库的不安全因素 非授权用户对数据库的恶意存取和破坏 数据库中重要或敏感的数据被泄露 安全环境的脆弱性 数据库安全性控制 用户身份鉴别 多层存取控制 审计 视图和数据加密 存取控制存取控制机制主要包括定义用户权限和合法权限检查两部分,定义用户权限和合法权限检查机制一起组成了数据库管理系统的存取控制子系统 C2级的数据库管理系统支持自主存取控制,B1级的数据库管理系统支持强制存取控制 在自主存取控制方法中,用户对于不同的数据库对象有不同的存取权限,不同的用户对同一对象也有不同的权限,而且用户还可将其拥有的存取权限转授给其它用户。因此自主存取控制非常灵活。 在强制存取控制方法中,每一个数据库对象被标以一定的密级,每一个用户也被授予某一个级别的许可证。对于任意一个对象,只有具体合法许可证的用户才可以存取。强制存取控制因此相对比较严格。 自主存取控制方法用户权限是由两个要素组成的:数据库对象和操作类型。定义一个用户的存取权限就是要定义这个用户可以在哪些数据库对象上进行哪些类型的操作。在数据库系统中,定义存取权限称为授权。在关系数据库系统中,存取控制的对象不仅有数据本身(基本表中的数据,属性列上的数据),还有数据库模式(包括数据库,基本表,视图和索引的创建等)。 关系数据库系统中的存取权限数据类型 数据库模式 模式:CREATE SCHEMA 基本表:CREATE TABLE, ALTER TABLE 视图:CREATE VIEW 索引:CREATE INDEX 数据 基本表和视图:SELECT, INSERT, UPDATE, DELETE, REFERENCES, ALL PRIVILEGES 属性列:SELECT, INSERT, UPDATE, REFERENCES, ALL PRIVILEGES 授权:授予与收回 GRANTGRANT语句的一般格式为: 1234GRANT <权限>,[,<权限>]...ON <对象类型> <对象名> [,<对象类型> <对象名>]...TO <用户>[,<用户>]...[WITH GRANT OPTION]; 其语义为:将对指定操作对象的指定操作权限授予指定的用户。发出该GRANT语句的还可以是数据库管理员,也可以是该数据库对象创建者(即属主owner),还可以是已经拥有该权限的用户。接受权限的用户可以是一个或多个具体用户,也可以是PUBLIC,及全体用户。如果指定了WITH GRANT OPTION子句,则获得某种权限的用户还可以把这种权限再授予其它的用户。如果没有指定WITH GRANT OPTION子句,则获得某种权限的用户只能使用该权限,不能传播该权限。 12345678GRANT UPDATE(Sno), SELECTON TABLE StudentTo U4GRANT INSERTON TABLE SCTO U5WITH GRANT OPTION REVOKEREVOKE的一般格式为; 1234567REVOKE <权限>[, <权限>]...ON <对象类型><对象名> [, <对象类型><对象名>]...FROM <用户>[,<用户>] ... [CASCADE|RESTRICT]REVOKE INSERTON TABLE SCFROM U5 CASCADE; 创建数据库模式的权限创建数据库模式一类的数据库对象的授权则由数据库管理员在创建用户时实现,创建用户的一般合适如下: 1CREATE USER <username> [WITH] [DBA|RESOURCE|CONNECT]; 只有系统的超级用户才有权创建一个新的数据库用户 新创建的数据库用户有三种权限:CONNECT, RESOURCE和DBA CREATE USER命令中如果没有指定创建的新用户的权限,默认该用户拥有CONNECT权限。拥有CONNECT权限的用户不能创建新用户,不能创建模式,也不能创建基本表,只能登陆数据库。由数据库管理员或其它用户授予他应用的权限,根据获得的授权的情况他可以对数据库对象进行权限范围内的操作。 拥有RESOURCE权限的用户能创建基本表和视图,称为所创建对象的属主,但不能创建模式,不能创建新的用户。数据库对象的属主可以使用GRANT语句把该对象上的存取权限授予其它用户。 拥有DBA权限的用户是系统中的超级用户,可以创建新的用户,创建模式,创建基本表和视图等;DBA拥有对所有数据库对象的存取权限,还可以把这些权限授予一般用户。 角色 角色的创建:CREATE ROLE <角色名> 给角色授权: 123GRANT <权限> [, <权限>]...ON <对象类型> 对象名To <角色> [,<角色>] 将一个角色授予其它的角色或用户 123GRANT <角色1> [, <角色2>]...To <角色3> [, <用户1>]...[WITH ADMIN OPTION] 如果指定了WITH ADMIN OPTION子句,则获得某种权限的角色或用户还可以把这种权限再授予其它的角色 角色权限的收回 REVOKE <权限> [, <权限>]... ON <对象类型> <对象名> FROM <角色> [,<角色>] REVOKE动作的执行者或者角色的创建者,或者拥有在这个角色上的ADMIN OPTION]]></content>
</entry>
<entry>
<title><![CDATA[百度音乐API]]></title>
<url>%2F2017%2F08%2F06%2Fnodejs%2F%E7%99%BE%E5%BA%A6%E9%9F%B3%E4%B9%90API%2F</url>
<content type="text"><![CDATA[百度音乐API 百度音乐全接口 会利用使用接口找歌简单又快捷 http://tingapi.ting.baidu.com/v1/restserver/ting 获取方式:GET参数:format=json或xml&calback=&from=webapp_music&method=以下不同的参数获得不同的数据PS:format根据开发需要可选择json或xmml,其他参数对应填入,calback是等于空的。 一、获取列表例:method=baidu.ting.billboard.billList&type=1&size=10&offset=0 参数: type = 1-新歌榜,2-热歌榜,11-摇滚榜,12-爵士,16-流行,21-欧美金曲榜,22-经典老歌榜,23-情歌对唱榜,24-影视金曲榜,25-网络歌曲榜 size = 10 //返回条目数量 offset = 0 //获取偏移 二、貌似是推广(无用)例:method=baidu.ting.adv.showlist&_=1430215999 参数:_ = 1430215999//时间戳 三、搜索例:method=baidu.ting.search.catalogSug&query=海阔天空 参数:query = ‘’ //搜索关键字 四、播放例:method=baidu.ting.song.play&songid=877578 例:method=baidu.ting.song.playAAC&songid=877578 参数:songid = 877578 //歌曲id 五、LRC歌词例:method=baidu.ting.song.lry&songid=877578 参数:songid = 877578 //歌曲id 六、推荐列表例:method=baidu.ting.song.getRecommandSongList&song_id=877578&num=5 参数: song_id = 877578 num = 5//返回条目数量 七、下载例:method=baidu.ting.song.downWeb&songid=877578&bit=24&_t=1393123213 参数: songid = 877578//歌曲id bit = 24, 64, 128, 192, 256, 320 ,flac//码率 _t = 1430215999,, //时间戳 八、获取歌手信息例:method=baidu.ting.artist.getInfo&tinguid=877578 参数: tinguid = 877578 //歌手ting id 九、获取歌手歌曲列表例:method=baidu.ting.artist.getSongList&tinguid=877578&limits=6&use_cluster=1&order=2 参数: tinguid = 877578//歌手ting id limits = 6//返回条目数量 其他就不用管了]]></content>
</entry>
<entry>
<title><![CDATA[数据库视图]]></title>
<url>%2F2017%2F08%2F05%2FDataBase%2F%E6%95%B0%E6%8D%AE%E5%BA%93%E8%A7%86%E5%9B%BE%2F</url>
<content type="text"><![CDATA[数据库视图 视图是从一个或几个基本表(或视图)导出的表。它与基本表不同,是一个虚表。数据库中只存放视图的定义,而不存放视图对应的数据,这些数据仍存放在原来的基本表中。 建立视图 sql语言用CREATE VIEW命令建立视图,其一般格式为: 123CREATE VIEW <视图名> [(<列名>[,<列名>]...)]AS <子查询>[WITH CHECK OPTION] 其中,子查询可以是任意的SELECT语句,是否可以含有ORDER BY子句和DISTINCT短语,取决于具体系统的实现。WITH CHECK OPTION表示对视图进行UPDATE, INSERT,和DELETE操作时要保证更新,插入或删除的行满足视图定义中的谓词条件(即子查询中的条件表达式),组成视图的属性列名或者全部省略或者全部指定,没有第三种选择。如果省略了视图的各个列名,则隐含该视图由子查询中SELECT子句目标列中的诸字段组成,但在下列情况下必须明确指定组成视图的所有列名。 1) 某个目标列不是单纯的属性名,而是聚集函数或列表达式 2) 多表连接时选出了几个同名列作为视图的字段 3) 需要在视图中为某个列启用新的更合适的名字 若一个视图是从单个基本表导出的,并且只是去掉了基本表的某些行和某些列,但保留了主码,则称这类视图为行列子集视图还可以用带有聚集函数和group by 子句的查询来定义视图,这种视图称为分组视图 12345CREATE VIEW S_G(Sno Gavg)ASSELECT Sno,AVG(Grade)FROM SCGROUP BY Sno; 删除视图 语句为 DROP VIEW <视图名> [CASCADE] ,视图删除后视图的定义将从数据字典中删除。如果该视图上还导出了其它视图,则使用CASCADE级联删除语句把该视图和由它导出的所有视图一起删除。 1DROP VIEW BT_S CASCADE; 查询视图 关系数据库管理系统执行对视图的查询时,首先进行有效性检查,检查查询中涉及的表,视图等是否存在。如果存在,则从数据字典中取出视图的定义,把定义中的子查询和用户的查询结合起来,转化成等价的对基本表的查询,然后再执行修正了的查询。这一转换过程称为视图消解 更新视图 更新视图是通过视图来插入(insert),删除(delete)和修改(update)数据。由于视图是不实际存储数据的虚表,因此对视图的更新最终要转化为对基本表的更新。像查询视图一样,对视图的更新操作也是通过视图消解,转化为对基本表的更新操作。 为防止用户通过视图对数据进行增加,删除,修改时,有意无意地对不属于视图范围的基本表数据进行操作,可在定义视图时加上WITH CHECK OPTION子句,这样在视图上增,删,改数据时,关系数据库管理系统会检查视图定义中的条件,若不满足条件则拒绝执行该操作。 视图的作用 视图能够简化用户的操作 视图使用户能以多种角度看待同一数据 视图对重构数据库提供了一定程度的逻辑独立性 视图能够对机密数据提供安全保护 适当利用视图可以更清晰地表达查询]]></content>
</entry>
<entry>
<title><![CDATA[string与stringstream]]></title>
<url>%2F2017%2F08%2F05%2Fc%E4%B8%8Ec%2B%2B%2Fstring%E4%B8%8Estringstream%2F</url>
<content type="text"><![CDATA[string与stringstream stringstring是c++提供的字符串型,和c的字符串相比,除了有不限长度的优点外,还有其它许多方便的功能。 首先加入#include\ ,声明变量可以写成string s;s=”hello”,也可以直接设置其值string s = “TCGS”,如果要取得其中一个字符,和传统的c语言一样是用s[i]取得。比较不一样的是如果s有三个字符,传统c的字符s[3]是0字元,但是c++的string则是只到s[2]这个字符而已。 操作: - 声明: + string:string s + 字符阵列:char s[100] - 取得第i个字符 + string:s[i] + 字符阵列:s[i] - 字符串长度 + string:s.length() + 字符阵列:strlen(s) - 读取一行 + string:getline(cin, s) + 字符阵列:gets(s) - 设成某字符串 + string:s="TSS" + 字符阵列:strcpy(s, 'TSS') - 字符串相加 + string:s = s + 'TSS' + 字符阵列:strcat(s, 'TSS') - 字符比较 + string:s=='TSS' + 字符阵列:strcmp(s, 'TSS') stringstreamstringstream是c++提供的另一个字符串型的串流物件,要使用stringstream,必须先加入这一行:#include,stringstream主要是用在将一个字符串分割,可以先用clear()以及str()将指定字符串设定成一开始的内容,再用>>把个别的资料输出,例如: 1234567string s;stringstream ss;int a,b,c;getline(cin, s);ss.clear();ss.str(s);ss>>a>>b>>c; 题目:输入的第一行有一个数字N代表接下来N行资料,每一行资料里有不固定个数的整数(最多20个,每行最大200个字符),请你写一个算法将每行的总和打印出来輸入: 31 2 320 17 23 54 77 60111 222 333 444 555 666 777 888 999 輸出: 62514995 程序:12345678910111213141516171819string s;stringstream ss;int n, i, sum, a;cin >> n;getline(cin, s); // 讀取換行for (i=0; i<n; i++){ getline(cin, s); ss.clear(); ss.str(s); sum=0; while (1) { ss >> a; if ( ss.fail() ) break; sum+=a; } cout << sum << endl;} 123456string常用的方法有size(),find(),substr()方法:string str = "yutyuyuyussdfas";size_t pos = str.find("ssdf", 3); //用if(pos==string::npos)用来判断是否找到字符串string str2 = str.substr(pos, 5);//find函数从str的第三个位置查起,找到sddf这个字符串后,返回字符串的位置。而substr函数从pos位置开始,截取5个字符,赋给str2 stringstream是字符串流,可以用来数据切分或者类型转化。123456string i2s (int i, int len = 0){ stringstream ss; ss << setw(len) << setfill('0') << i; return ss.str();}//以i2s(7, 3)形式调用这个函数,返回的结果是字符串007 string到int的转换1234string res = "1000";int n = 0;stream<<result;stream>>n; 在类型转化中使用模板。to_string()函数将t转化为字符串并写入result中。使用str()成员函数来获取流内部缓存的一份拷贝: 123456789template <class T>void to_string(string &result, const T &l){ ostringstream oss; //创建一个流 oss<<t; //把值传递到流中 result = oss.str(); //获取转换后的字符并将其写入result}to_string(s1, 10.5); //double到stringto_string(s1, 123); //int到string 可以更进一步定义一个通用的转化模板,用于任意类型之间的转换。函数模板convert()含有两个模板参数out_type和in_type,功能是将in_value值转换成out_type类型: 1234567891011121314template <class out_type, class in_value>out_type convert (const in_value &t){ stringstream stream; stream << t; //向流中传值 out_type result; //这里存储转换结果 stream>>result; //向result中写入值 return result;}double d;string salary;string s = "23.323";s = convert<double>(s);salary = convert<string>(9000.0); 在进行多次转换的时候,必须调用stringstream的成员函数clear()函数1234567891011121314#include <sstream>#include <iostream>int main(){ std::stringstream stream; int first, second; stream<< "456"; //插入字符串 stream >> first; //转换成int std::cout << first << std::endl; stream.clear(); //在进行多次转换前,必须清除stream stream << true; //插入bool值 stream >> second; //提取出int std::cout << second << std::endl;}]]></content>
</entry>
<entry>
<title><![CDATA[C++的find函数]]></title>
<url>%2F2017%2F08%2F05%2Fc%E4%B8%8Ec%2B%2B%2FC%2B%2B%E7%9A%84find%2F</url>
<content type="text"><![CDATA[C++的find函数 头文件#include 函数实现12345678template <class InputIterator, class T>InputIterator find (InputIterator first, InputIterator last, const T&val){ while (first!=last){ if(*first==val) return first; ++first; } return last;} 例1123456789101112131415#include <iostream>#include <algorithm>#include <vector>using namespace std;int main(){ vector<string> m; m.push_back("hello"); m.push_back("hello2"); m.push_back("hello3"); if (find(m.begin(), m.end(), "hello") == m.end()) cout << "no" << endl; else cout << "yes" << endl;} 例212345678910111213141516#include <iostream>#include <algorithm>#include <string>#include <set>using namespace std;int main(){ set<string> m; m.insert("hello"); m.insert("hello2"); m.insert("hello3"); if (find(m.begin(), m.end(), "hello") == m.end()) cout << "no" << endl; else cout << "yes" << endl;} set,string自身有个find()函数 1234567891011121314151617181920212223242526272829303132#include <iostream>#include <algorithm>#include <string>#include <set>using namespace std;int main(){ set<string> m; m.insert("hello"); m.insert("hello2"); m.insert("hello3"); if (find(m.begin(), m.end(), "hello") == m.end()) cout << "no" << endl; else cout << "yes" << endl; //find函数返回类型 size_type string s("1a2b3c4d5e6f7g8h9i1a2b3c4d5e6f7g8ha9i"); string flag; string::size_type position; //find 函数 返回jk 在s 中的下标位置 position = s.find("jk"); if (position != s.npos) //如果没找到,返回一个特别的标志c++中用npos表示,我这里npos取值是4294967295, { cout << "position is : " << position << endl; } else { cout << "Not found the flag" + flag; }}]]></content>
</entry>
<entry>
<title><![CDATA[SQL数据定义语句]]></title>
<url>%2F2017%2F08%2F04%2FDataBase%2Fsql%E6%95%B0%E6%8D%AE%E5%AE%9A%E4%B9%89%E8%AF%AD%E5%8F%A5%2F</url>
<content type="text"><![CDATA[SQL数据定义语句 关系数据库系统支持三级模式结构,其模式,外模式和内模式中的基本对象有模式,视图和索引等。因此SQL的数据定义功能包括模式定义,表定义,视图和索引的定义。 sql的数据定义语句 模式 创建:CREATE SCHEMA 删除:DROP SCHEMA 表 创建:CREATE TABLE 删除:DROP TABLE 修改:ALTER TABLE 视图 创建:CREATE VIEW 删除:DROP VIEW 索引 创建:CREATE INDEX 删除:DROP INDEX 修改:ALTER INDEX 一个关系数据库管理系统的实例中可以建立多个数据库,一个数据库中可以建立多个模式,一个模式下通常包括多个表,视图和索引等数据库对象。 模式的定义与删除 定义模式 CREATE SCHEMA <模式名> AUTHORIZATION <用户名>,如果没有指定<模式名>,那么<模式名>隐含为<用户名>。要创建模式,调用该命令的用户必须拥有数据库管理员权限,或者获得了数据库管理员授予的CREATE SCHEMA的权限。目前,在CREATE SCHEMA中可以接受CREATE TABLE,CREATE VIEW和GRANT,也就是说用户可以在创建模式的同时在这个模式定义中进一步创建基本表,视图,定义授权。即:CREATE SCHEMA <模式名> AUTHORIZATION <用户名> [ <表定义子句>|<视图定义子句>|<授权定义子句> ]; 删除模式 DROP SCHEMA <模式名> ,其中cascade和restrict两者必选其一。选择了cascade(级联),表示在删除模式的同时把该模式中所有的数据库对象全部删除;选择了restrict(限制),表示如果该模式中已经定义了下属的数据库对象(如表,视图等),则拒绝该删除语句的执行。只有当该模式中没有任何下属的对象时才能执行drop schema语句。 基本表的定义,删除与修改 定义基本表 创建了一个模式就建立了一个数据库的命名空间,一个框架。在这个空间中首先要定义的是该模式包含的数据库基本表。SQL语句使用CREATE TABLE语句定义基本表,其基本格式为: CREATE TABLE <表名> (<列名><数据类型> [列级完整性约束条件] [,<列名><数据类型>[列级完整性约束条件]]) ........... [,<表级完整性约束条件>] 建表的同时通常还可以定义与该表有关的完整性约束条件,这些完整性约束条件被存入系统的数据字典中,当用户操作表中数据时由关系数据库管理系统自动检查该操作是否违背这些完整性约束条件。 如果完整性约束条件涉及该表的多个属性列,则必须定义在表级上,否则既可以定义在列级也可以定义在表级 CREATE TABLE SC ( Sno CHAR(9), Cno CHAR(4), Grade SMALLINT, PRIMARY KEY (Sno, Cno), /* 主码由两个属性构成,必须作为表级完整性进行定义 */ FOREIGN KEY (Sno) REFERENCES Student (Sno), /* 表级完整性约束条件,Sno是外码,被参照表是Student */ FOREIGN KEY (Cno) REFERENCES Course (Cno) /* 表级完整性约束条件,Cno是外码,被参照表是Course */ ) 数据类型 CHAR(N),CHARACTER(N):长度为n的定长字符串 VARCHAR(N):最大长度为n的变长字符串 CLOB:字符串大对象 BLOB:二进制大对象 INT,INTEGER:长整数(4字节) SMALLINT:短整形(2字节) BIGINT:大整形(8整数) NUMERIC(p,d):定点数,由p位数字(不包括符号,小数点)组成,小数点后面有d位数字 DECIMAL(p,d):同NUMERIC REAL:取决于机器精度的单精度浮点数 DOUBLE PRECISION:取决于机器精度的双精度浮点数 FLOAT(n):可选精度的浮点数,精度至少为n位数字 BOOLEAN:逻辑布尔值 DATE:日期,包含年,月,日,格式为yyyy-mm-dd TIME:时间,包含一日的时,分,秒,格式为HH:MM:SS TIMESTAMP:时间戳类型 INTERVAL:时间间隔类型 修改基本表 ALTER TABLE <表名> [ADD [COLUMN] <新列名> <数据类型> [完整性约束]] [ADD <表级完整性约束>] [DROP [COLUMN] <列名> [CASCADE | RESTRICT]] [DROP CONSTRAINT <完整性约束名> [RESTRICT|CASCADE]] [ALTER COLUMN <列名><数据类型>] 删除基本表 DROP TABLE <表名> [RESTRICT|CASCADE]。 索引的建立与删除 建立索引 CREATE [UNIQUE] [CLUSTER] INDEX <索引名> ON <表名>(<列名> [<次序>] [,<列名> [<次序>]] ...) UNIQUE:表明此索引的每一个索引值只对应唯一的数据记录 CLUSTER:表示要建立的索引是聚簇索引 修改索引 ALTER INDEX <旧索引名> RENAME TO <新索引名> 删除索引 DROP INDEX <索引名> 数据查询 SELECT [ALL|DISTINCT] <目标列表达式> [, <目标列表达式>] … FROM <表名或视图名> [,<表名或视图名>] | () [AS] <别名> [WHERE <条件表达式>] [GROUP BY <列名 1> [HAVING <条件表达式>]] [ORDER BY <列名 2> [ASC|DESC]] %(百分号):代表任意长度(长度可以为0)的字符串。_(下横线):代表任意单个字符]]></content>
</entry>
<entry>
<title><![CDATA[关系的完整性]]></title>
<url>%2F2017%2F08%2F03%2FDataBase%2F%E5%85%B3%E7%B3%BB%E7%9A%84%E5%AE%8C%E6%95%B4%E6%80%A7%2F</url>
<content type="text"><![CDATA[关系的完整性 关系模型的完整性规则是对关系的某种约束条件。也就是说关系的值随着时间变化时因该满足一些约束条件。这些约束条件实际上是现实世界的要求。任何关系在任何时刻都要满足这些语义约束。 关系模型中有三类完整性约束:实体完整性,参照完整性和用户定义的完整性 。其中实体完整性和参照完整性是关系模型必须满足的完整性约束条件,被称为关系的两个不变性,应该由关系系统自动支持。用户定义的完整性是应用领域需要遵循的约束条件,体现了具体领域中的语义约束。 实体完整性实体完整性规则:若属性(指一个或一组属性)A是基本关系R的主属性,则A不能取空值(null value)。所谓空值就是“不知道”或“不存在”或“无意义”的值。 实体完整性规则说明如下: 实体完整性规则是针对基本关系而言的。一个基本表通常对应现实世界的一个实体集。 现实世界中的实体是可区分的,即他们具有某种唯一性标识。 相应的,关系模型中以主码作为唯一性标识。 主码中的属性即主属性不能取空值。如果主属性取空值,就说明存在某个不可标识的主体,即存在不可区分的实体,这与第2点相矛盾,因此这个规则称为实体完整性。 设F是基本关系R的一个或一组属性,但不是关系R的码,Ks是基本关系S的主码。如果F与Ks相对应,则称F是R的外码,并称基本关系R为参照关系,基本关系S为被参照关系或目标关系。关系R和S不一定是不同的关系。 参照完整性若属性(属性组)F是基本关系R的外码,它与基本关系S的主码Ks相对应(基本关系R与S不一定是不同的关系),则对于R中的每个元组在F上的值必须: 或者取空值(F的每个属性值均为空值) 或者等于S中某个元组的主码值。 用户定义的完整性 任何关系数据库系统都应该支持实体完整性和参照完整性。这是关系模型所要求的。除此之外,不同的关系数据库系统根据其应用环境的不同,往往还需要一些特殊的约束条件。用户定义的完整性就是针对某一具体关系数据库的约束条件,它反应某一具体应用所涉及的数据必须满足的语义要求。 关系模型应提供定义和检验这类完整性的机制,以便用统一的系统的方法处理它们,而不需要由应用程序承担这一功能]]></content>
</entry>
<entry>
<title><![CDATA[module的加载实现]]></title>
<url>%2F2017%2F07%2F31%2FJavaScript%2Fmodule%E7%9A%84%E5%8A%A0%E8%BD%BD%E5%AE%9E%E7%8E%B0%2F</url>
<content type="text"><![CDATA[module的加载实现 浏览器加载 传统方法 在HTML网页中,浏览器通过script标签加载JavaScript脚本1234567<!-- 页面内嵌的脚本 --><script type="application/javascript"> //module code</script><!-- 外部脚本 --><script type="application/JavaScript" src="path/to/Module.js"></script> 上面代码中,由于浏览器的脚本的默认语言是JavaScript,因此 type=”application/javascript” 可以省略。 默认情况下,浏览器同步加载JavaScript脚本,即渲染引擎遇到script标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。 下面有两种异步加载的方法 12<script src="mymodule.js" defer></script><script src="mymodule.js" async></script> 上面代码中,script标签打开defer或async属性,脚本就会异步加载。渲染引擎遇到这一命令,就会开始下载外部脚本,但不会等他下载和执行,而是直接执行后面的命令。 defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。 加载规则 浏览器加载ES6模块,也使用script标签,但要加入type=”module”属性。 上面代码在网页插入foo.js,由于type属性设为module,所以浏览器知道这是一个ES6模块。 浏览器对于带有type=”module”的script,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,在执行模块脚本,等同于打开了script标签的defer属性。 123<script type="module" src="foo.js"></script><!-- 等同于 --><script type="module" src="foo.js" defer></script> script标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,在恢复渲染。 ES6模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。 1234<script type="module"> import utils from './util.js' // other code</script> 对于外部的模块的脚本,有几点注意。 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。 模块脚本自动采用严格模式,不管有没有声明use strict 模块之中,可以使用import命令加载其它模块( .js后缀不可省略,需要提供绝对url与相对url ),也可以使用export命令输出绝对接口。 模块之中,顶层的this关键字返回undefined,而不是window。也就是说,在模块顶层使用this关键字,是无意义的。 同一个模块如果加载多次,将只执行一次 12345678import utils from 'https://example.com/js/utils.js';const x = 1;console.log(x === window.x); //falseconsole.log(this === undefined); // truedelete x; // 句法错误,严格模式禁止删除变量 利用顶层的this等于undefined这个语法点,可以侦测当前代码是否在es6模块之中 const isNotModuleScript = this !== undefined ES6模块与CommonJs模块的差异 它们有两个重大差异 CommonJs 模块输出的是一个值的拷贝,ES6模块输出的是一个值的引用。 CommonJs 模块是运行时加载,ES6模块是编译时输出接口 第二个差异是因为CommonJs加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。 下面重点解释第一个差异。 CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个模块文件lib.js的例子。123456789// lib.jsvar counter = 3;function incCounter() { counter++;}module.exports = { counter: counter, incCounter: incCounter,}; 上面代码输出内部变量counter和改写这个变量的内部方法incCounter。然后,在main.js里面加载这个模块。 12345678910111213141516171819// main.jsvar mod = require('./lib');console.log(mod.counter); // 3mod.incCounter();console.log(mod.counter); // 3上面代码说明,lib.js模块加载以后,它的内部变化就影响不到输出的mod.counter了。这是因为mod.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。// lib.jsvar counter = 3;function incCounter() { counter++;}module.exports = { get counter() { return counter }, incCounter: incCounter,}; 上面代码中,输出的counter属性实际上是一个取值器函数。现在再执行main.js,就可以正确读取内部变量counter的变动了。 123$ node main.js34 ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。 export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。123456789101112// mod.jsfunction C() { this.sum = 0; this.add = function () { this.sum += 1; }; this.show = function () { console.log(this.sum); };}export let c = new C(); 上面的脚本mod.js,输出的是一个C的实例。不同的脚本加载这个模块,得到的都是同一个实例。 123456789101112131415// x.jsimport {c} from './mod';c.add();// y.jsimport {c} from './mod';c.show();// main.jsimport './x';import './y';现在执行main.js,输出的是1。$ babel-node main.js1 这就证明了x.js和y.js加载的都是C的同一个实例。]]></content>
</entry>
<entry>
<title><![CDATA[Vue的非父子组件通信]]></title>
<url>%2F2017%2F07%2F30%2FVue%2F%E9%9D%9E%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1%2F</url>
<content type="text"><![CDATA[Vue的非父子组件通信 可以通过vuex和event bus来解决 Demo我们要实现的效果是: 上下分别为foo组件和bar组件,他们之间是非父子关系,分别点击各自的button,另一个组件的count对应增加。 1234567891011121314<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>非父子组件通信</title></head><body> <div id="app"> <foo></foo> <hr> <bar></bar> </div></body></html> JavaScript的实现是: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253//注册一个空的Vue实例,作为“中转站”var eventBus = new Vue({})//foo组件var foo = { template: `<div><p>the count of foo is {{fooCount}}</p> <button @click="addBar">add bar's count</button> </div>`, data () { return { fooCount: 0 } }, methods: { addBar () { eventBus.$emit('addBar',1) } }, mounted () { eventBus.$on('addFoo', function (num) { this.fooCount += num }.bind(this)) //这里必须将this绑定到组件实例上,如果不使用bind,也可以使用箭头函数。 }}//bar组件var bar = { template: `<div><p>the count of bar is {{fooCount}}</p> <button @click="addBar">add foo's count</button> </div>`, data () { return { barCount: 0 } }, methods: { addFoo () { eventBus.$emit('addFoo',1) } }, mounted () { eventBus.$on('addBar', function (num) { this.barCount += num }.bind(this)) //这里必须将this绑定到组件实例上,如果不使用bind,也可以使用箭头函数。 }}var vm = new Vue ({ el: '#app', components: { foo, bar }}) 以上就实现了一个简易的非父子组件之间的通信方式。通过event bus,在一个组件创建时的钩子函数中监听某个事件,而在需要与其进行通信的组件中触发这个函数,同时交换数据。]]></content>
</entry>
<entry>
<title><![CDATA[Git的reset与checkout的区别]]></title>
<url>%2F2017%2F07%2F29%2Fgit%2Fgit%E7%9A%84reset%E4%B8%8Echeckout%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[Git的reset与checkout的区别 Head,Index,Working Director Git里面的三个重要区域 HEAD指向最近一次commit里的所以snapshot Index 缓存区域,只有Index区域里的东西才可以被commit Working Directory 用户操作区域 初始状态当你checkout分支的时候,git做了这么三件事情 > 将HEAD指向那个分支的最后一次commit > 将HEAD指向的commit里所有的文件的snapshot替换掉Index区域里面的内容 > 将Index区域里面的内容填充到Working Directory里 所以你可以发现,HEAD,Index,Working Directory这个时候里的内容都是一摸一样的。 注意:一般会误解为,Index中的内容是空的,只有git add后才会有东西。实际上不是,Index里一直是有东西的。 ,所以,git里的所有操作就是对这三个区域的状态的操作。 Changed如果你在Working Directory里修改了文件,git会发现Working Directory里面的内容与Index区域里面的内容不一致了。这个时候git status的结果就是:Changes not staged for commit Staged一个文件仅仅changed是不能够被commit的,git要求只能提交Index里的东西。所以需要git add,这个命令的意思是,把changed的文件的内容同步到Index区域里。这样Working Directory和Index区域的内容就一致了,这个过程称之为stage,这个时候git status的结果是:Changes to be commited Commited最后,就可以提交了git commit,这样,就把HEAD的状态和Index以及Working Directory就形成了一致了。 resetreset是用来修改提交历史的,想象这种情况,如果你在2天前提交了一个东西,突然发现这个提交是有问题的。这个时候你有两个选择,要么使用git revert(推荐),要么使用git reset。 上图可以看到git reset是会修改版本历史的,他会丢弃掉一些版本历史。而git revert是根据那个commit逆向生成一个新的commit,版本历史是不会破会的。 已经push到远程仓库的commit不允许reset上面已经讲了,git reset是会丢弃掉commit的。如果commit已经被push到远程仓库上了,也就意味着其它开发人员就可能基于这个commit形成了新的commit,这时你去reset,就会造成其它开发人员的提交历史莫名其妙的丢失,或者其它灾难性的后果。 因此,一旦commit已经被push到远程仓库,那么是坚决不允许去reset它的。 不带文件参数的reset前面章节已经说到Git有三个区域,Git的所有操作实际上是在操作这三个区域的状态(或内容)。git reset配合不同的参数,对这三个区域会产生不同的影响。reset实际上有三个步骤,根据不同的参数可以决定执行哪个步骤(–soft,–mixed,–hard)。 1. 改变HEAD所指向的commit(--soft) 2. 执行第一步,将Index区域更新为HEAD所指向的commit里包含的内容(--mixed) 3. 执行第1,2步,将Working Directory区域更新为HEAD所指向的commit里包含的内容(--hard) 带文件参数的reset上面讲到的git reset实际上不带参数的,那带上文件参数呢? HEAD不会动 将那个commit的snapshot里的文件放到Index区域中 需要注意的是带文件参数的git reset没有–hard, –soft这两个参数。只有–mixed参数。 unstage12git reset file.txtgit reset --mixed HEAD file.txt 上面这两条命令是一样的,都是reset到HEAD上,这个例子的意义在于,unstage file,当你把一个文件stage到Index区域后悔了,那么只需把Index区域里的这个文件恢复到最近一次commit的状态(也就是HEAD),那就相当于unstage。 恢复到历史版本下面这个命令就是将某个文件恢复到历史版本上。git reset eb23er file.txt这个例子的意思在于,把某个文件恢复到Index区域里,然后直接commit,这样就等于把这个文件恢复到历史版本了,这样依赖你都不需要改动working Directory了。 checkout前面讲到的checkout是会修改HEAD的指向,变更Index区域的内容,修改Working Directory里的内容。这看上去很像reset –hard,但两者有两个重要的差别 reset会把working directory里面所有的内容都更新掉,checkout不会去修改你在working Directory里修改过的文件。 reset把branch移动到HEAD指向的地方,checkout则把HEAD移动到另一个分支。 第二个区别可能有点那以理解,举例来说:假若你有两个分支master和develop,这两个分支指向不一样的commit,我们现在在develop分支上(HEAD指向的地方) 如果我们git reset master,那么develop就会指向master所指向的那个commit。如果我们git checkout master,那么develop不会动,只有HEAD会移动。HEAD会指向master。看图: 带文件参数当执行git checkout [branch] file,checkout干了这件事情: 更新了index区域里面的file文件的内容 更新了working directory里file文件的内容]]></content>
</entry>
<entry>
<title><![CDATA[自制滚动条]]></title>
<url>%2F2017%2F07%2F28%2FCSS3%2F%E8%87%AA%E5%88%B6%E6%BB%9A%E5%8A%A8%E6%9D%A1%2F</url>
<content type="text"><![CDATA[自制滚动条 前沿webkit支持拥有overflow属性的区域,列表框,下拉菜单,textarea的滚动条的自定义样式。 滚动条的组成 ::-webkit-scrollbar 滚动条整体部分 ::-webkit-scrollbar-thumb 滚动条里面的小方块,能向上向下移动 ::-webkit-scrollbar-track 滚动条的轨道 ::-webkit-scrollbar-button 滚动条的轨道的两端按钮,允许通过点击微调小方块的位置 ::-webkit-scrollbar-track-piece 内层轨道,滚动条中间部分除去 ::-webkit-scrollbar-corner边角,即两个滚动条的交汇处 ::-webkit-resizer 两个滚动条的交汇处上用于通过拖到调整元素大小的控件 1234567891011121314151617181920212223/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar { width: 16px; height: 16px; background-color: #F5F5F5; } /*定义滚动条轨道 内阴影+圆角*/ ::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); border-radius: 10px; background-color: #F5F5F5; } /*定义滑块 内阴影+圆角*/ ::-webkit-scrollbar-thumb { border-radius: 10px; -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); background-color: #555; } 详细设置定义滚动条就是利用伪元素与伪类,那什么是伪元素与伪类呢?伪类如:link,:focus,:hover,此外,css3中又增加了许多伪类选择器,如:nth-child,:last-child,:nth-last-of-type等。 CSS中的伪元素大家以前看过,:first-line,:first-letter,:before,:after。那么在CSS3中,伪元素进行了调整,在以前的基础上增加了一个 “:”,也就是变成了 “::first-line,::first-letter,::before,::after”,另外,CSS3还增加了一个”::selection”。两个”:”和一个”::”,在CSS3中主要用来区分伪类和伪类选择器。 webkit的伪类和伪元素的实现很强,可以把滚动条当成一个页面元素来定义,再结合一些高级的CSS3属性,比如渐变,圆角,RGBA等等。如果有些地方要用图片,可以把图片转换成Base64,不然每次都得加载那个图片,增加请求数。 任何对象都可以设置:边框,阴影,背景图片等等,创建的滚动条任然会按照操作系统本身的设置来完成其交互的行为。下面的伪类可以应用到上面的伪元素中。 12345678910111213141516171819202122232425262728293031:horizontal //horizontal伪类适用于任何水平方向上的滚动条 :vertical //vertical伪类适用于任何垂直方向的滚动条 :decrement //decrement伪类适用于按钮和轨道碎片。表示递减的按钮或轨道碎片,例如可以使区域向上或者向右移动的区域和按钮 :increment //increment伪类适用于按钮和轨道碎片。表示递增的按钮或轨道碎片,例如可以使区域向下或者向左移动的区域和按钮 :start //start伪类适用于按钮和轨道碎片。表示对象(按钮 轨道碎片)是否放在滑块的前面 :end //end伪类适用于按钮和轨道碎片。表示对象(按钮 轨道碎片)是否放在滑块的后面 :double-button //double-button伪类适用于按钮和轨道碎片。判断轨道结束的位置是否是一对按钮。也就是轨道碎片紧挨着一对在一起的按钮。 :single-button //single-button伪类适用于按钮和轨道碎片。判断轨道结束的位置是否是一个按钮。也就是轨道碎片紧挨着一个单独的按钮。 :no-button no-button伪类表示轨道结束的位置没有按钮。 :corner-present //corner-present伪类表示滚动条的角落是否存在。 :window-inactive //适用于所有滚动条,表示包含滚动条的区域,焦点不在该窗口的时候。 ::-webkit-scrollbar-track-piece:start { /*滚动条上半边或左半边*/ } ::-webkit-scrollbar-thumb:window-inactive { /*当焦点不在当前区域滑块的状态*/ } ::-webkit-scrollbar-button:horizontal:decrement:hover { /*当鼠标在水平滚动条下面的按钮上的状态*/ }]]></content>
</entry>
<entry>
<title><![CDATA[Vue实例与生命周期]]></title>
<url>%2F2017%2F07%2F28%2FVue%2FVue%E5%AE%9E%E4%BE%8B%E4%B8%8E%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%2F</url>
<content type="text"><![CDATA[Vue实例与生命周期 Vue的实例是Vue框架的入口,其实也就是前端的viewModel,它包含了页面中的业务逻辑,数据模型等,当然他也有自己的一系列的生命周期的事件钩子,辅助我们进行对整个Vue实例生成,编译,挂载,销毁等过程进行JavaScript控制。 Vue实例初始化的选项配置对象详解 Vue实例的data对象 Vue的实例的数据对象data我们已经用了很多,数据绑定离不开data里的数据。他也是Vue里的核心属性。它是Vue绑定数据到HTML标签的数据源泉,另外Vue框架会自动监视data里面的数据变化,自动更新数据到HTML标签上去。本质原理是:Vue会自动将data里面的数据进行递归抓取换成getter和setter,然后就可以自动更新HTML标签了,所以用getter和setter老的浏览器Vue支持不够好。 data对象的类型 类型是Object或者Function。 如果是组件对象,data必须是Function类型 实例12345678910111213//创建普通的Vue实例var vm = new Vue({ data: data })//组件定义,Vue.extend()中data必须是函数var component = Vue.extend({ data: function () { //这里必须是函数!!! return { a: 1 } } }) Vue实例的computed 介绍 Vue的计算属性会自动混入Vue的实例中。所有getter和setter的this上下文会自动地绑定为Vue实例。 类型 {键:函数} {[key:string]: Function | {get: Function, set: Function}} 。 当然,可以省略setter和getter,那么值就可以是普通的函数,但是必须有返回值。 实例 12345678910111213141516var vm = new Vue({ data: {a: 1}, computed: { aDouble () { return this.a * 2 }, aPlus () { set () { return this.a + 1 }, get () { thia.a = v - 1 } } } }) methods 类型 {[key: string] : Function} 详细 methods 将被混入到Vue实例中。可以直接通过Vm实例访问这些方法,或者在指令表达式中使用。方法中的this自动绑定为Vue实例。注意:不应该使用箭头函数来定义method函数(例如 plus: ()=> this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以this将不会按照期望指向Vue实例,this.a将会是undefined。 实例 12345678910var vm = new Vue({ data: {a: 1}, methods: { plus () { this.a++ } } })vm.plus()vm.a watch 类型 {[key: string]: string | Function | Object} 详细: 一个对象,键是需要观察的表达式,值是对应回掉函数,也可以是方法名,或者包含选项的对象。Vue实例将会在实例化时调用$watch,遍历watch对象的每一个属性。 实例: 1234567891011121314151617var vm = new Vue({ data: { a: 1, b: 2, c: 3 }, watch: { //监控a变量变化时,自动执行此函数 a: function (val, oldVal) { console.log("new : %s, old: %s", val,oldVal) }, c: { handle: function (val, oldVal){}, deep: true } }}) 设置el的详解 类型 string | HTMLElement 限制:只在由new创建的实例中遵循 详细 提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标,也就是说Vue绑定数据到哪里去找。可以是css选择器,也可以是一个HTMLElement实例。在实例挂载之后,元素可以用vm.$el访问。如果这个选项在实例化时有作用,实例将立即进入编译过程,否则,需要显示调用vm.$mount()手动开始编译。 实例 1234var vm = new Vue({ el: '#app', ....}) Vue实例的生命周期 Vue实例有一个完整的生命周期,也就是从开始创建,初始化数据,编译模板,挂载DOM,渲染,更新,卸载等一系列过程,我们称这为Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。 在Vue的整个生命周期中,它提供了一系列的事件,可以让我们达到控制整个过程的目的,在这些事件响应方法中的this直接指向的是Vue的实例。 Vue提供的可以注册的钩子函数在图上的红色框标注: beforeCreate:在实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用。 created:实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,watch/event事件的回调。然而,挂载阶段还没开始,$el属性目前不可见。 beforeMount:在挂载开始之前被调用:相关的render函数首次被调用。 mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.$el也在文档内。 beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。你可以在这个钩子中进一步更改状态,这不会触发附加的重渲染过程。 updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于DOM的操作。然而在大多数情况下,你应该避免再次期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。 beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。 destroyd: Vue实例销毁后调用。调用后,Vue实例指示的所有的东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。 实例1 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue生命周期</title> <script src="https://unpkg.com/vue/dist/vue.js"></script></head><body> <div id="app"> <p>{{ number }}</p> <input type="text" v-model="number"> </div> <script> var app = new Vue({ el: '#app', data: { number: 1 }, beforeCreate: function () { console.log('beforeCreate 钩子执行...'); console.log(this.number) }, cteated: function () { console.log('cteated 钩子执行...'); console.log(this.number) }, beforeMount: function () { console.log('beforeMount 钩子执行...'); console.log(this.number) }, mounted: function () { console.log('mounted 钩子执行...'); console.log(this.number) }, beforeUpdate: function () { console.log('beforeUpdate 钩子执行...'); console.log(this.number) }, updated: function () { console.log('updated 钩子执行...'); console.log(this.number) }, beforeDestroy: function () { console.log('beforeDestroy 钩子执行...'); console.log(this.number) }, destroyed: function () { console.log('destroyed 钩子执行...'); console.log(this.number) }, }); </script></body></html> 实例2 12345678910111213141516171819import Axio from 'axios' //这是一个轻量级的ajax库,import是es6模块导入的语法export default{ name: 'app', component: {}, data: function () { return { list: [] } }, mounted: function () { //挂载完成后的生命周期钩子注册 this.$nextTick(function () { //等待下一次更新完成后执行业务处理代码 Axio.get('/api/menulist',{ //将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新 params:{} }).then(function () { this.list = res.data }.bind(this)) }) }} Vue实例的全局配置 silent 类型:boolean 默认值:false 用法:= truelink123456789101112131415- optionMergeStrategies + 类型:{[key: string]: Function} + 默认值:{} + 用法: ```JavaScript Vue.config.optionMergeStrategies._my_option = function (parent, child, vm){ return child+1 } const Profile = Vue.extend({ _my_option: 1 }) //Profile.options._my_option = 2 //自定义合并策略的选项 //合并策略选项分别接受第一个参数作为父实例,第二个参数为子实例,Vue实例上下文被作为第三个参数传入 devtools 类型:boolean 默认值:true (生产版为false) 用法: 123//务必在加载Vue之后,立即同步设置以下内容Vue.config.devtools = true//配置是否允许vue-devtools检查代码。开发版本默认为true,生产版本默认为false errorHandle 类型:Function 默认值:默认抛出错误 用法: 123Vue.config.errorHandler = function (err, vm){ //指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和Vue实例。 //handle error} keyCodes 类型:{[key:string]: number | Array} 默认值: {} 用法: 123456Vue.config.keyCodes = { v: 86, f1: 112, mediaPlayPause: 179, up: [38, 87]} Vue的全局API Vue.nextTick([{callback: Function} , {context: Object}]) 用法:在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM 123456//修改数据vm.msg = "hello"//DOM还没有更新Vue.nextTick(function () { //DOM更新了 }) Vue.set([{object: Object}, {key: string}, {value: any}]) 用法:设置对象的属性,如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新。这个方法主要用于避开Vue不能检测属性被添加的限制。注意对象不能是Vue实例,或者是Vue实例的根数据对象。 Vue.compile 语法:Vue.compile(template) 参数:{template: string} 用法: 123456789//在render函数中编译模板字符串。只在独立构建时有效var res = Vue.compile("<div>{{msg}}</div>")new Vue({ data: { msg: 'hello' }, render: res.render, staticRenderFns: res.staticRenderFns})]]></content>
</entry>
<entry>
<title><![CDATA[javaScript获取css样式]]></title>
<url>%2F2017%2F07%2F17%2FJavaScript%2FjavaScript%E8%8E%B7%E5%8F%96css%E6%A0%B7%E5%BC%8F%2F</url>
<content type="text"><![CDATA[javaScript获取css样式 css样式分为行内样式与外部样式 javaScript获得行内样式 可以使用ele.style.属性名称(如果遇到属性名称带有‘-’,需要使用驼峰方法,例如background-color改为backgroundColor); javaScript获得外部样式(getComputedStyle可以获得style的值也可以获得外部样式表的css) 获得外部样式可以使用浏览器提供的window.getComputedStyle(ele,null),这里的ele就是需要操作的对象,第二个参数是指定一个伪元素匹配,常规的元素用不上,直接使用null。但这个getComputedStyle并不支持IE9以下的浏览器,但是ie有它自己支持的方法:ele.currentStyle; ele.style.属性名和ele.cssText以及getComputedStyle(obj,null)有什么区别? ele.style.属性名:这里获得的style可以获得属性值,也可以进行修改 ele.cssText: 其实跟style差不多,只不过它是获得多个css样式,也是生成在行内样式中。 getComputedStyle(obj,null): 只能获取不能修改,并且返回的css是一个CSSStyleDecoration对象集合。]]></content>
</entry>
<entry>
<title><![CDATA[javascript事件解读]]></title>
<url>%2F2017%2F07%2F16%2FJavaScript%2FjavaScript%E4%BA%8B%E4%BB%B6%E8%A7%A3%E8%AF%BB%2F</url>
<content type="text"><![CDATA[javascript事件解读 与浏览器进行交互的时候浏览器会触发各种事件。这样,我们就可以编写javascript,通过监听某一个事件,来实现某些功能扩展。例如监听load事件,显示欢迎信息, 基础事件操作 监听事件 浏览器会根据某些操作触发对应事件,如果我们需要针对某些事件进行处理,则需要监听这个事件。监听事件主要有以下几种方法: HTML内联属性 HTML元素里面直接填写事件有关属性,属性值为javascript代码,即可在触发该事件的时候,执行属性值的内容。 例如:<button onclick="alert('click')"></button>,onclick属性表示触发click,属性值的内容会在单击该HTML节点时执行。显而易见,使用这种方法,javascript代码与HTML代码耦合在了一起,不便于维护与开发。所以除非在必须使用的情况下(例如统计连接点击数据)下,尽量避免使用这种方法。 DOM属性绑定 也可以直接设置DOM属性来指定某个事件对应的处理函数,这个方法比较简单:element.onclick = function(){alert('click');},上面代码就是监听element节点的click事件。他比较简单易懂,而且有较好的兼容性。但是也有缺陷,因为直接赋值给对应属性,如果你在后面代码中再次为element绑定一个回调函数,会覆盖掉之前回调函数的内容。 虽然也可以用一些方法实现多个绑定,但还是推荐下面的标准事件监听函数。 使用事件监听函数 标准的事件监听函数如下: element.addEventListener(<event-name>, <callback>, <use-capture>)。表示在element这个对象上面添加一个事件监听器,当监听到有事件发生的时候,调用这个回调函数。至于这个参数,表示该事件监听是在“捕获”阶段中监听(设置为true)还是在“冒泡”阶段中监听(设置为false)。关于捕获与冒泡,会在下面讲解。 用标准事件监听函数改写上面的例子:var btn = document.querySelector('button);btn.addEventListener('click',function(){alert('click');},false);。 移除事件监听 当我们为某个元素绑定了一个事件,每次触发这个事件的时候,都会执行事件绑定的回调函数。如果我们想解除绑定,需要使用removeEventListener方法:element.removeEventListener(<event-name>,<callback>,<use-capture>),需要注意的是,绑定事件时的回调函数不能是匿名函数,必须是一个声明的函数,因为解除事件绑定时需要传递这个回调函数的引用,才可以断开绑定。例如:var fun = function(){};element.addEventListener('click',fun,false); element.removeEventListener('click',fun,false);。 事件触发过程 捕获阶段 当我们在DOM树的某个节点发生了一些操作(例如单击,鼠标移动上去),就会有一个事件发射过去。这个事件从window发出,不断经过下级节点直到目标节点。在到达目标节点之前的过程,就是捕获阶段。所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回Window。监听某个在捕获阶段触发的事件,需要在事件监听函数传递第三个参数true。element.addEventListener(<event-name>, <callback>,true),但一般我们使用false,会在后面说明。 目标阶段 当事件传递到触发目标节点那里,最终在目标节点上触发这个事件,就是目标阶段。需要注意的是,事件触发的目标总是在最底层的节点。 冒泡阶段 当事件到达目标节点之后,就会沿着原路返回,由于这个过程类似于水泡从底部浮动到顶部,所以称之为冒泡阶段。 **在实际使用中,你并不需要把事件监听函数准确绑定到最底层的节点也可以正常工作。比如在上例,你想为绑定单击时的回调函数,你无须为这个下面的所有子节点全部绑定单击事件,只需要为这一节点绑定即可。因为发生它子节点的单击事件,都会冒泡上去,发生在上面。 为什么不用第三个参数true? 这时因为IE浏览器不支持在捕获阶段监听事件,为了统一而设置的,毕竟IE浏览器的份额是不可忽略的。 使用事件代理提升性能 因为事件有冒泡机制,所有子节点的事件都会顺着父级节点跑回去,所以我们可以通过监听父级节点来实现监听子节点的功能,这就是事件代理。 使用事件代理的优势: 减少了事件绑定,提升性能。之前你需要绑定一堆子节点,而现在你只需要绑定一个父节点即可。减少了绑定事件监听函数的数量。 动态变化的DOM结构,任然可以监听。当一个DOM动态创建之后,不会带有任何事件监听,除非你重新执行监听事件函数,而使用事件监听无需担忧这个问题。为了简单,使用jquery来实现普通事件绑定和事件处理。目标是监听所有a连接的单击事件。1. 常规的事件绑定方法,jquery会循环每一个a结构并绑定事件监听函数。 2. 事件监听的方法,jquery只为父元素绑定事件监听函数,因为父元素下面会有很多无关节点也会触发click事件,所以在on函数里传递了第二个参数,表示只监听a子节点的事件。*他们都可以正常工作,但是当我动态创建新DOM结构的时候,第一个方法问题出现了,新创建的结构没有绑定事件,所以无法执行回调函数。而第二个方法工作很好,因为点击新建的DOM,它的事件会冒泡到父级节点进行处理。*如果使用原生的方法实现事件代理,需要注意过滤非目标节点,可以通过id,class或者tagname等等,例如:ele.addEventListener('click',function(event){ //判断是否为a节点 if(event.target.tagName=='A'){ // 执行a的交互}},false)。 停止事件冒泡 所有的事情都会有对立面,事件的冒泡阶段虽然看起来很好,也会有不适用的场所。比较复杂的应用,由于事件监听比较复杂,可能会希望只监听发生在具体节点的事件。这个时候就需要停止事件冒泡。停止事件冒泡要使用事件对象的stopPropagation方法,具体代码如下:ele.addEventListener('click',function(e){ e.stopPropagation(); },false)。 事件Event对象 事件对象包括很多有用的信息,比如事件触发时,鼠标在屏幕上的坐标,被触发的DOM的详细信息,以及一些常用方法与属性。 type: String 事件的名称,比如’click’ target: Node 事件要触发的目标节点 bubbles: Boolean 表示该事件是否在冒泡阶段触发的 preventDefault: function 这个方法可以禁止一切默认的行为,例如点击a标签时,会打开一个新的页面,如果为a标签监听事件click同时调用该方法,则不会打开新的页面。 stopPropagation: function 停止冒泡 cancelable: Boolean 这个属性表明该事件是否可以通过event.preventDefault()方法来禁用默认行为。 eventPhase: Number 这个属性的数字表示当前事件触发在什么阶段。none: 0,捕获:1,目标:2,冒泡:3。 isTrusted: Boolean 表明该事件是浏览器触发(用户真实操作触发)。 常用事件和技巧 用户的操作有很多种,所以有很多事件。为了开发方便,浏览器又提供了一些事件,所以有很多的事件。这里只介绍几种常用的事件和使用技巧。 load: load事件在资源加载完成时触发。这个资源可以是图片,css文件,js文件,视屏,document和window等等。比较常用的就是监听window的load事件,当页面内所有资源加载完成之后就会触发。比如用javaScript对图片以及其它资源处理,我们在load事件中触发,可以保证javaScript不会在资源未加载完成就开始处理资源导致报错。同样的,也可以监听图片等其它资源加载情况。 beforeunload: 当浏览者在页面的输入框输入一些内容时,未保存,误操作关掉网页会导致输入信息丢失。 利用javaScript模拟触发内置事件。 内置的事件也可以被javaScript模拟触发,比如下面函数模拟触发单击事件 function simulate(){ var event = new MouseEvent('click',{ 'view': window, 'bubbles':true, 'cancelable': true }); } var cb = document.getElementById('checkbox'); var canceled = !cb.dispatchEvent(event); if(canceled){ alert('canceled'); // A handle called preventDefault }else{ alert('not canceled'); //None of the handles called preventDefault } 自定义事件我们可以自定义事件来实现更灵活的开发,事件用好了可以是一个强大的工具。与自定义相关的函数有Event,CustomEvent和dispatchEvent。直接自定义事件,使用event构造函数。 // Create the event. var event = document.createEvent('Event'); // Define that the event name is 'build'. event.initEvent('build', true, true); // Listen for the event. elem.addEventListener('build', function (e) { // e.target matches elem }, false); // target can be any Element or other EventTarget. elem.dispatchEvent(event);]]></content>
</entry>
<entry>
<title><![CDATA[strcpy与strncpy用法与区别]]></title>
<url>%2F2017%2F07%2F14%2Fc%E4%B8%8Ec%2B%2B%2Fstrcpy%E4%B8%8Estrncpy%E7%94%A8%E6%B3%95%E4%B8%8E%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[strcpy与strncpy用法与区别 strcpy函数:把从src地址开始含有NULL结束符的字符串复制到以dest开始的地址空间,返回dest。要求:src和dest所指的内存区域不可以重叠且dest必须有足够的空间来容纳src字符串。函数原型为: extern char strcpy(char dest, char src); 1234567char *strcpy( char* strDest, const char *strSrc){ char *strDestCopy = strDest; if((strDest==NULL)||(strSrc==NULL)) throw "Invalid arguments"; while((*strDest++ = *strSrc++)!='\0'); return strDestCopy;} 该函数的参数是字符指针,也就是可以是字符串变量和字符数组,因为它们的变量名代表字符地址。字符串默认有一个以null的结束符,字符数组没有。所以此处需要注意:因为src要求有null结束符,所以字符数组的长度必须大于等于src包含null结束符的总长度。例如,char *src=”abcd”;char dest[5] ;这里的dest的长度至少为5; strncpy函数:n代表可以指定字符个数进行赋值。原型:char strncpy(char dest, char *src, size_t n);功能:将字符串src最多n个字符复制到字符数组dest中(它并不像strcpy一样遇到NULL才停止复制,而是等凑够n个字符才开始复制),返回指向dest的指针。要求:如果n > dest串长度,dest栈空间溢出产生崩溃异常。 src串长度 <= dest串长度(这里的串长度包含串尾NULL字符)如果 n = (0, src串长度),src的前n个字符复制到dest中。但是由于没有NULL字符,所以直接访问dest会发生乱码。这时,一般建议采用memset将dest的全部元素用null填充,如memset(dest, 0, 7) 如果n=src串长度,与strcpy一致。如果n=dest串长度,[0, src串长度]处存放src字符串,(src串长度,dest串长度]处存放NULL。 src串长度 > des串长度如果n = dest串长度,则dest串没有null字符,会导致输出有乱码。如果不考虑src串复制完整性,可以将dest最后一个字符置为NULL。 所以,一般把n设为dest(含null)的长度,当n=dest串长度时,定义dest为字符数组,因为这时没有null字符拷贝。]]></content>
</entry>
<entry>
<title><![CDATA[数据库语言分类]]></title>
<url>%2F2017%2F07%2F11%2FDataBase%2F%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AF%AD%E8%A8%80%E5%88%86%E7%B1%BB%2F</url>
<content type="text"><![CDATA[关系数据语言分类 早期的关系操作能力通常用代数方式或逻辑方式来表示,分别称为 关系代数 和 关系演算 。关系代数用对关系的运算来表达查询要求,关系演算则用谓词来表达查询要求。关系演算又可按谓词变元的基本对象是元组变量还是域变量分为 元组关系演算 和 域关系演算 。一个关系数据语言能够表示关系代数可以表示的查询,称为具有完备的表达能力,简称关系完备性。已经证明关系代数,元组关系演算和域关系演算三种语言在表达能力上是等价的,都具有完备的表达能力。另外,还有一种介于关系代数和关系演算之间的结构化查询语言,sql不仅具有丰富的查询功能,而且具有数据定义和数据控制功能,是集查询,数据定义语言,数据操纵语言和数据控制语言于一体的关系数据语言,他充分体现了关系数据语言的特点和优点,是关系数据库的标准语言。 因此,关系数据语言可以分为三类: 关系数据语言: 1. 关系代数语言(例如:ISBL) 2. 关系演算语言 1. 元组关系演算语言(例如ALPHA,QUEL) 2. 域关系演算语言(例如QBE) 3. 具有关系代数和关系演算双重特点的语言(例如SQL) SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL。 数据查询语言DQL数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE 子句组成的查询块: SELECT <字段名表> FROM <表或视图名> WHERE <查询条件> 数据操纵语言DML数据操纵语言DML主要有三种形式: 1) 插入:INSERT 2) 更新:UPDATE 3) 删除:DELETE 数据定义语言DDL数据定义语言DDL用来创建数据库中的各种对象—–表、视图、 索引、同义词、聚簇等如: CREATE TABLE/VIEW/INDEX/SYN/CLUSTER 表 视图 索引 同义词 簇 DDL操作是隐性提交的!不能rollback 数据控制语言DCL 数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制 数据库操纵事务发生的时间及效果,对数据库实行监视等。如: 1) GRANT:授权。 2) ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。 回滚—ROLLBACK 回滚命令使数据库状态回到上次最后提交的状态。其格式为: SQL>ROLLBACK; 3) COMMIT [WORK]:提交。 在数据库的插入、删除和修改操作时,只有当事务在提交到数据 库时才算完成。在事务提交前,只有操作数据库的这个人才能有权看 到所做的事情,别人只有在最后提交完成后才可以看到。 提交数据有三种类型:显式提交、隐式提交及自动提交。下面分 别说明这三种类型。 (1) 显式提交 用COMMIT命令直接完成的提交为显式提交。其格式为: SQL>COMMIT; (2) 隐式提交 用SQL命令间接完成的提交为隐式提交。这些命令是: ALTER,AUDIT,COMMENT,CONNECT,CREATE,DISCONNECT,DROP, EXIT,GRANT,NOAUDIT,QUIT,REVOKE,RENAME。 (3) 自动提交 若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后, 系统将自动进行提交,这就是自动提交。其格式为: SQL>SET AUTOCOMMIT ON;]]></content>
</entry>
<entry>
<title><![CDATA[Proc/C++开发]]></title>
<url>%2F2017%2F07%2F11%2FDataBase%2FProc%2F</url>
<content type="text"><![CDATA[Proc/C++开发 基本概念 SQL 结构化查询语言,简称SQL,是一种数据库查询和程序设计语言,用于存取数据以及查询,更新和管理关系数据库系统;同时,也是数据库脚本文件的扩展名。 Pro*C/C++ 在C/C++程序中嵌入SQL语句操作数据库,得到的应用程序叫做Proc*C/C++程序。 嵌入式SQL 能在其它编程语言中混合使用的SQL语句,叫做嵌入式SQL语句!但是各个厂商对嵌入式SQL的具体实现不一样,但是具体的混合语法不一样,各厂商有自己的预编译工具。 宿主语言 嵌入式SQL的载体是宿主语言,比如这里说的就是C/C++。 宿主语言 Pro程序 C/C++ Pro*C/C++ FORTRAN Pro*FORTRAN PASCAL Pro*PASCAL COBOL Pro*COBOL PL/I Pro*PL/I Ada Pro*Ada 程序结构 Include 头文件(c/c++ and Pro*c/c++) 定义变量 定义函数 main4.1 连接数据库 :connect4.2 SQL操作语句:EXEC SQL …..4.3 exception handle4.4 断开连接4.5 EXEC SQL COMMIT / ROLLBACK WORK release 代码1234567891011121314151617181920212223242526272829#include <stdio.h>#include <string.h>#include <stdlib.h>#include "sqlca.h"EXEC SQL BEGIN DECLARE SECTION char username[32]; char passwd[32]; char dname[25];EXEC SQL END DECLARE SECTIONEXEC SQL INCLUDE sqlca;void sqlerror();void main(){ EXEC SQL WHENEVER SQLERROR DO sqlerror(); strcpy(username, "scott"); strcpy(passwd, "XXXXX"); EXEC SQL CONNECT :username IDENTIFIED BY :passwd; EXEC SQL select dname from dept where id=10; printf("dname: %s\n",dname);}void sqlerror(){ EXEC SQL WHENEVER SQLERROR CONTINUE; printf("\n oracle error detected\n"); printf("%.70s\n",sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit(1);} 开发流程 一般应用程序c开发运行标准流程 一般应用程序c++开发运行标准流程 Pro*C/C++ 应用程序的开发多了一个预编译的过程:pc转换成c或cpp文件 关于预编译工具(Proc) 位置:$ORACLE_HOME/bin 目录下拥有数据库访问权限的用户可以直接在终端使用,不需要绝对路径。 配置文件:$ORACLE_HOME/precomp/admin/pcscfg.cfg 头文件,库文件路径。 基本命令格式:proc iname=filename oname=outname [OptionName1=value1]…[OptionNameN=valueN] 基本选项: 选项 说明 INAME path and filename (name of input file) 1.pc ONAME path and filename (name of output file) 1.c INCLUDE path (头文件所在路径) -INCLUDE 路径名 或 INCLUDE= (路径名1,路径名2) PARSE FULL 、PARTIA、NONE (DEFAULT FULL for C,Others for C++) CODE ANSI_C、CPP (default ansi_c) USERID username/password 默认预编译得到的是C文件,使用下列选项得到C++文件parse=none 告诉proc编译器,按照C++规范解析 dm02_hello.pccode = cpp 告诉proc编译器,按照C++规范生成文件proc dm02_hello.pc parse=none code=cpp oname=dm02_hello.cpp 相关操作 连接数据库 方法一EXEC SQL CONNECT :usr_pwd 方法二EXEC SQL CONNECT :username IDENTIFIED BY :passwd 方法三通过db_name连接没有限定名字的数据库–可以修改数据库的名字–at选项EXEC SQL CONNECT :username IDENTIFIED BY :passwd AT :db_name USING :db_string 代码里写死了用户名和密码 12345678910111213141516171819202122232425#include <stdio.h>#include <stdlib.h>#include <string.h>#include "sqlca.h"//定义宿主变量(SQL变量)//C语言可以直接使用//嵌入式SQL语句里面使用的话需要加上EXEC SQL前缀EXEC SQL BEGIN DECLARE SECTION;char * serverid = "scott/lzj123529";EXEC SQL END DECLARE SECTION;int main(){ int ret = 0; printf("HelloWorld\n"); EXEC SQL connect:serverid; if(sqlca.sqlcode != 0) { ret = sqlca.sqlcode ; printf("ret :%d\n",ret); return ret; } printf("connect OK!\n"); EXEC SQL COMMIT RELEASE;//提交事务并且断开 return 0;} 交互式的连接数据库 12345678910111213141516171819202122232425EXEC SQL BEGIN DECLARE SECTION;char user[32];char passwd[32];char sid[32];EXEC SQL END DECLARE SECTION;int main(){ int ret = 0; printf("user:"); scanf("%s",user); printf("passwd:"); scanf("%s",passwd); printf("sid:"); scanf("%s",sid); EXEC SQL CONNECT :user IDENTIFIED BY :passwd USING :sid; if(sqlca.sqlcode != 0) { ret = sqlca.sqlcode ; printf("ret :%d\n",ret); return ret; } printf("connect OK!\n"); EXEC SQL COMMIT RELEASE;//提交事务并且断开 return 0;} 通过宿主变量指定连接名字 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465//演示通过程序连接多个数据库EXEC SQL BEGIN DECLARE SECTION; char *usrname = "scott"; char *passwd = "tiger"; char *link1 = "link1"; //通过宿主变量指定连接名字 char *serverid = "orcl"; char *usrname2 = "scott"; char *passwd2 = "tiger"; char *link2 = "link2"; char *serverid2 = "orcl";EXEC SQL END DECLARE SECTION;int main(){ int ret = 0; //第一个用户连接数据库 EXEC SQL CONNECT :usrname IDENTIFIED BY :passwd AT :link1 USING :serverid ; if (sqlca.sqlcode != 0) { ret = sqlca.sqlcode; printf("第一个用户连接数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode); return ret; } else { printf("第一个用户连接数据库 成功connect ok...\n"); } //第二个用户连接数据库 EXEC SQL CONNECT:usrname2 IDENTIFIED BY:passwd2 AT:link2 USING:serverid2 ; if (sqlca.sqlcode != 0) { ret = sqlca.sqlcode; printf("第二个用户连接数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode); return ret; } else { printf("第二个用户连接数据库 成功connect ok...\n"); } //断开连接 EXEC SQL AT:link1 COMMIT RELEASE; if (sqlca.sqlcode != 0) { ret = sqlca.sqlcode; printf("第1个用户断开数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode); return ret; } else { printf("第1个用户断开数据库 成功 RELEASE ok...\n"); } EXEC SQL AT:link2 COMMIT RELEASE; if (sqlca.sqlcode != 0) { ret = sqlca.sqlcode; printf("第二个用户断开数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode); return ret; } else { printf("第二个用户断开数据库 成功 RELEASE ok...\n"); } return ret ;} 宿主变量 C的数据类型不同于Oracle的数据类型,在数据传递时有一个数据类型转换过程。在Proc*C/C++程序中,是被C和SQL语句使用的变量,位于 123EXEC SQL BEGIN DECLARE SECTION.............EXEC SQL END DECLARE SECTION 之内。 主要类型: 程序中形式 说明 char 单字符 char[N] N个定长字符数组 short 短整型 long 长整型 float 单精度浮点数 double 双精度浮点数 VARCHAR[N] 变长字符串 使用场景 输入:将应用程序的数据传递到数据库中 1234int salary,emp_number;cin>>salary;cin>>emp_number;EXEC SQL update emp set sal= :salary where empno= :emp_number; 输出:将数据库的数据传递到应用程序中。 1234float v_salary;char v_job;EXEC SQL select sal,job INTO :v_salary, :v_job from emp where empno=90;cout<< v_salary<<v_job; 申明语法与普通C变量一致,但在CODE=CPP或MODE=ANSI时变量必须放在申明区。 可使用Pointer作为宿主变量,使用前分配空间。 在数据定义语言(DDL)语句中不能用宿主变量。错误例子: 123char table_name[30];cin>> table_name;EXEC SQL DROP TABLE :table_name 关于数据的增删改查 预先定义了12345EXEC SQL BEGIN DECLARE SECTION;int deptid = 50;char dname[32] = "20name";char loc[32] = "20loc";EXEC SQL END DECLARE SECTION; 插入数据 EXEC SQL insert into dept(DEPTNO,DNAME,LOC) values(:deptid,:dname,:loc); 删除数据 EXEC SQL delete from dept where deptno=:deptid; 修改数据 EXEC SQL update dept set loc = :loc where deptno=:deptid; 综合示例: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include "sqlca.h"//定义宿主变量(SQL变量)//C语言可以直接使用//嵌入式SQL语句里面使用的话需要加上EXEC SQL前缀EXEC SQL BEGIN DECLARE SECTION;//char * serverid = "scott/lzj123529";char user[32];char passwd[32];char sid[32];int deptid = 50;char dname[32] = "20name";char loc[32] = "20loc";EXEC SQL END DECLARE SECTION;int main(){ int ret = 0; printf("HelloWorld\n"); printf("\nuser:"); scanf("%s",user); printf("\npasswd:"); scanf("%s",passwd); printf("sid:"); scanf("%s",sid); EXEC SQL CONNECT:user IDENTIFIED BY:passwd USING:sid; if(sqlca.sqlcode != 0) { ret = sqlca.sqlcode ; printf("ret :%d\n",ret); return ret; } printf("connect OK!\n"); EXEC SQL insert into dept(DEPTNO,DNAME,LOC) values(:deptid,:dname,:loc); EXEC SQL COMMIT;//提交事务不退出 sleep(10); printf("delete ....\n"); //EXEC SQL delete from dept where deptno=:deptid; strcpy(loc,"中国"); EXEC SQL update dept set loc = :loc where deptno=:deptid; EXEC SQL COMMIT RELEASE;//提交事务并且断开 return 0;}]]></content>
</entry>
<entry>
<title><![CDATA[数据库范式]]></title>
<url>%2F2017%2F07%2F01%2FDataBase%2F%E6%95%B0%E6%8D%AE%E5%BA%93%E8%8C%83%E5%BC%8F%2F</url>
<content type="text"><![CDATA[数据库范式 关系数据库中的关系是要满足一定要求的,满足不同程度要求的为不同范式。它是英国人E.F.Codd在上个世纪70年代提出关系数据库模型后总结出来的。目前有迹可寻的共用8种范式,依次是1NF,2NF,3NF,BCNF,4NF,5NF,DKNF,6NF。通常所用到的只是前三个范式,即:第一范式,第二范式,第三范式。 一个低一级范式的关系模式通过模式分解可以转化为若干个高一级范式的关系模式的集合,这种过程叫做规范化。 第一范式(1NF):强调的是列的原子性,即列不能够再分成其它几列。 第二范式(2NF):首先是1NF,另外包含两部分内容,一是表必须有一个主键,二是没有包含在主键中的列必须完全依赖于主键,而不能依赖于主键的一部分。 第三范式(3NF):首先是2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列A依赖于非主键列B,非主键列B依赖于主键的情况。 第二范式和第三范式的概念很容易混淆,区分它们的关键点在于,2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分;3NF:非主键列是直接依赖于主键,还是传递依赖于主键。]]></content>
</entry>
<entry>
<title><![CDATA[数据结构常用内排序]]></title>
<url>%2F2017%2F06%2F27%2FDataStructure%2F%E5%B8%B8%E7%94%A8%E5%86%85%E6%8E%92%E5%BA%8F%2F</url>
<content type="text"><![CDATA[数据结构常用内排序 插入排序 直接插入排序:依次将待排序序列中的每一条记录插入到一个已经排好序的序列中,直到全部的记录都排好序。在最好情况下,待排序序列为正序,每趟只需与有序序列的最后一个记录的关键码比较。总的比较次数为n-1,因此,时间复杂度为O(n)。在最坏情况下,待排序序列为逆序,第i个记录必须与前i-1个记录的关键码做比较,并且每比较一次就要做一次记录的移动,则移动的次数为 : $\sum_{i=1}^n n= \frac{n(n-1)}{2}$,因此,时间复杂度为$O(n^2)$。 直接插入排序是一种稳定的排序方法 12345678910void InsertSort(int r[],int len){ for(int i = 1;i<len;i++){ int temp = r[i]; int j; for(j = i-1;j>=0&&r[j]>temp;j--){ r[j+1] = r[j]; } r[j+1] = temp; }} 希尔排序:先将整个待排序记录序列分割成若干个子序列,在子序列内分别进行直接插入排序,待整个序列基本有序时,再对全体记录进行一次直接插入排序。希尔排序算法的时间性能分析是一个复杂的问题,因为它是所取增量的函数。有人在大量实验的基础上指出,希尔排序的时间性能在$O(n^2)$和O(nlog2n)。希尔排序是一种不稳定的排序方法 123456789101112void ShellSort(int r[],int n){ for(int d = n/2;d>=1;d = d/2){ for(int j = d;j<=n;j++){ int temp = r[j]; int i; for(i = j-d;i>=0&&r[i]>temp;i = i-d){ r[i+d] = r[i]; } r[i+d] = temp; } }} 交换排序 起泡排序:两两比较相邻记录的关键码,如果反序则交换,直到没有反序的记录为止。在最好的情况下,待排序的记录为正序时,算法只执行一趟,进行了n-1次关键码的比较,不需要移动记录,时间复杂度为O(n),在最坏的情况下,待排序的记录为反序,每趟排序在无序序列中只有一个最大的记录被交换到最终位置,故算法执行n-1趟,第i(1<=i<n)趟排序执行了n-i次关键码的比较和n-i次记录的交换,这样,关键码的比较次数为$\sum_{i=1}^{n-1} (n-i)= \frac{n(n-1)}{2}$,因此,时间复杂度为$O(n^2)$。起泡排序是一种稳定的排序方法。 123456789101112131415void Bubble(int r[],int len){ int exchange = len; while(exchange!=0){ int bound = exchange; exchange = 0; for(int i = 0;i<bound-1;i++){ if(r[i]>r[i+1]){ int temp = r[i]; r[i] = r[j+1]; r[j+1] = temp; exchange = i+1; } } }} 快速排序:首先选取一个轴值(即比较的基准),将待排序的记录划分为两个独立的两部分,左侧记录的关键码均小于或等于轴值,右侧记录的关键码均大于或等于轴值,然后分别对这两部分重复上述过程。快速排序的平均复杂度为O(nlog2n) 快速排序是一种不稳定的排序方法 123456789101112131415161718192021222324252627282930/* 快速排序的一次划分算法 */int partition(int r[],int first,int end){ int i = first,j = end-1; while(i<j){ while(i<j&&r[i]<=r[j]) j--; if(i<j){ int temp = r[i]; r[i] = r[j]; r[j] = temp; i++; } while(i<j&&r[i]<r[j]) i++; if(i<j){ int temp = r[i]; r[i] = r[j]; r[j] = temp; j--; } }}/* 快速排序算法*/ void quickSort(int r[],int len){ int pivot = partition(r,0,len-1); quickSort(r,0,pivot-1); quickSort(r,pivot+1,len-1); } 选择排序 简单选择排序:第i趟排序在待排序序列r[i]~rn中选取关键码最小的记录,并和第i个记录交换作为有序序列的第i个记录。平均复杂度为$O(n^2)$,选择排序是一种不稳定的排序方法 123456789101112131415void selectSort(int r[],int len){ for(int i = 0;i<len-1;i++){ int index = i; for(int j = i+1;j<len;j++){ if(r[j]>r[index]){ index = j; } } if(index!=i){ int temp = r[i]; r[i] = r[index]; r[index] = temp; } }} 堆排序:首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最大值即堆顶记录,然后将该堆顶记录移走,并将剩余的记录再调整成堆,这样又找出了次大的记录。依次类推,直到堆中只有一个记录为止。总的时间复杂度为O(nlog2n)。 堆排序是一种不稳定的排序方法 1234567891011121314151617181920212223242526void shift(int r[],int k,int m){ int i = k,j = 2*i; while(j<m){ if(r[j]<r[j+1]) j++; if(r[i]>r[j]) break; else{ int temp = r[i]; r[i] = r[j]; r[j] = temp; i = j; j = 2*i; } }}void HeapSort(int r[], int n){ for(int i = n/2;i>=1;i--){ shift(r,i,n); } for(int i = 1;i<n;i++){ int temp = r[1]; r[1] = r[n-i+1]; r[n-i+1] = temp; shift(r,1,n-i); }}]]></content>
</entry>
<entry>
<title><![CDATA[使用Docker]]></title>
<url>%2F2017%2F05%2F04%2Fdocker%2F%E4%BD%BF%E7%94%A8Docker%2F</url>
<content type="text"><![CDATA[使用Docker 使用Docker]]></content>
</entry>
<entry>
<title><![CDATA[docker简介]]></title>
<url>%2F2017%2F05%2F03%2Fdocker%2Fdocker%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[docker简介 docker简介Docker是Docker公司2013年3月推出的开源容器项目,上市至今已有3年,是世界范围内拥有超高人气。进入2010年,服务器市场急速向云环境转移。人们开始更多的租用虚拟服务器,只需要缴纳一定的租金。不需要购买实际的物理服务器。尤其是在搭建物理服务器时,服务器硬件的购买及其安装都需要耗费相当长的时间。但在云环境下,无论是1台还是1000台,只需单击几次即可轻松搭建虚拟服务器。 创建虚拟服务器后,还要在其中安装各种软件,进行各种设置。如果只有一两台服务器。那么能够进行轻松设置;但随着服务器数量的增加,采用人工设置就难了。linux/Unix环境中,虽然可以借助沿用至今的shell脚本进行自动安装与设置,但这种方式有一定的局限性。使用shell脚本很难实现集中式管理功能与其它复杂功能。 此时出现了“不可变基础设施”,指的是主机OS与服务器运行环境(服务器程序,源代码,已编译的二进制文件)分离,只设置一次运行环境,之后不发生变更。也就是说,将服务器运行环境创建为镜像后,部署至各服务器运行。此时若服务器更新,则运行环境本身不会发生变更,只要重新生成镜像并再次部署即可。就像云平台对服务器“用过即扔”,不可变基础设施中的服务运行环境镜像也是用过一次后就扔掉。 不可变基础设施拥有多种优点: 管理方便 : 由于服务运行环境以镜像形式存在,所以只要管理镜像本身即可。特别是可以集中管理镜像,实现系统部署与管理。此外,镜像生成设置也以文件形式存在,可以灵活用于版本管理系统。 拓展 : 可以利用一个镜像不断创建服务器。与云平台的自动伸缩功能配合使用,能够轻松实现服务扩展。 测试 : 只要在开发人员PC或测试服务器中运行镜像,就可以搭建与实际服务运行环境一致的环境,非常易于测试。 轻量 : 分离操作系统与服务运行环境,实现轻量化,提供可以随时运行的环境。 Docker项目实现了不可变基础设施。 虚拟机与dockerDocker与我们之前使用的VMware、Microsoft Hyper-V (Virtual PC)、Xen、Linux KVM等虚拟机类似。在虚拟机中安装Linux后,可以安装各种服务器程序与DB,运行已开发的应用程序与网站。将搭建好的虚拟机镜像复制到多台服务器运行,之后即可用一个镜像不断创建服务器。 虚拟机 虚拟机非常方便,但性能不佳。当前许多CPU都添加了对虚拟化功能的大量支持,但与物理机器相比,虚拟机的运行速度比较慢。 为了进一步改善“完全虚拟化”的运行速度,半虚拟化技术登场,现在正得到广泛应用。 虚拟机本身是一台完整的计算机,总是需要安装客户OS。由于镜像中含有OS,所以镜像体积会变大。 无论网速多快,收发虚拟机镜像都会非常耗时,尤其是开源虚拟化软件。其重点在于OS虚拟化,只提供镜像创建与运行功能,在部署与管理功能上存在不足。 Docker 与半虚拟化相比,Docker是一种更轻量化的方式。使用Docker则不需要安装客户OS。Docker镜像中只隔离并安装服务器运行所需的程序与库,与主机共享OS资源(系统调用),这样就大大减小了镜像的体积。 Docker没有硬件虚拟化层,所以与虚拟机相比,其在内存访问,文件系统,网络速度上明显快得多。与虚拟机不同,Docker提供了专门创建并部署镜像的功能。如同在git中管理源代码一样,Docker也提供镜像版本管理功能。此外,为了进行集中管理,Docker也提供了镜像的上传与下载功能(push/pull)。就像GitHub一样,Docker Hub 提供帮助用户共享的Docker镜像。 Docker镜像与容器 Docker中有“镜像”与“容器”的概念。首先了解“基础镜像”,它是指仅Linux发行版userland中安装的文件,一般为Linux发行版本的名称。此外,Linux发行版userland中也有安装Redis或Nginx等的基础镜像。因此,一般所说的“Docker镜像”是指安装基础镜像所需的程序,库,源代码之后创建的一个文件。 我们很容易想到,若每次都安装基础镜像所需的程序,库,源代码,就会出现大容量镜像重复创建。Docker镜像只将基础镜像中变化的部分创建为镜像,运行时将基础镜像与可变部分合并运行。 userland:以内存使用为基准,OS可化为内核空间与用户空间,用户空间中运行的可执行文件与库称为userland。Linux不能只从内核启动,所以userland也指启动时所需的最少可执行文件与库的组合。Linux发行版本中,userland通常包含启动所需的可执行文件,库以及原有的包系统。 Docker不会创建整个镜像,而只针对变化的部分进行创建,然后继续引用父镜像。这种工作方式在Docker中称为“层”。Docker镜像是文件,所以上传到存储空间后可以在别处下载使用。并且,向存储空间上传时,会同时上传子镜像与父镜像;同样,下载时也会同时操作子镜像与父镜像,之后只传输内容有变化的镜像。 Docker容器是处于运行状态的镜像,使用一个镜像可以创建多个容器。从操作系统角度看,镜像是可执行的文件,容器是进程。而已经运行的容器中,也可以将更改的部分创建为镜像。可以将Docker视为特定执行文件或脚本的运行环境。 linux/Unix系列系统中,文件运行所需的所有组成元素被切割的很小。这样虽然可以使系统结构简单明了,但会导致过度依赖,也很难解决。因此,Linux发行版也单独提供经过编译的包(rpm,deb包)。但每当运行服务器时,要想逐个编译源代码或安装并设置已有包也是相当麻烦的。 如果只有一两台服务器,则不会有太大困难,但云环境中创建的服务器多达几十台,几百台。此时若使用已经完成服务器配置的Docker镜像,那么无论要运行多少台服务器都能轻松搞定。 安装docker Ubuntu安装 123sudo apt-get updatesudo apt-get install docker.iosudo ln -sf /usr/bin/docker.io /usr/local/bin/docker RedHat Enterprise Linux、CentOS CentOS 6:sudo yum install http://dl.fedoraproject.org/pub/epel/6/x86-64/epel-release-6-8.noarch.rpm。 CentOS 7:sudo yum install docker。 启动Docker服务:sudo service docker start。 启动时自动运行:sudo chkconfig docker on。]]></content>
</entry>
<entry>
<title><![CDATA[Git分支操作]]></title>
<url>%2F2017%2F04%2F24%2Fgit%2Fgit%E5%88%86%E6%94%AF%2F</url>
<content type="text"><![CDATA[Git分支操作 这篇文章主要说明如何使用分支使我们的开发工作更加顺滑。 git branch 用法12345git branch //列出所有分支git branch <branch> //创建名为<branch>的分支,但是不会切换过去git branch -d <branch> //删除指定分支,这是一个安全操作,git会阻止你删除包含未合并更改的分支git branch -D <branch> //强制删除分支git branch -m <branch> //重新命名当前分支 在日常开发中,无论是修复一个我们不稳定bug或者是添加一个功能,我们都应该新建一个分支来封装我们的修改。这样可以保证我们不稳定的代码永远不会提交到主分支上。 创建分支 分支只是指向提交的指针,当你创建新分支,实际上只是创建了一个新的指针,仓库本身不会受到影响,一开始你的仓库只有一条分支: 然后你执行下面的命令创建一个分支,用于加一个新的feature: 当然执行后,你只是创建了这个分支,还需要执行git checkout new-feature切换到new-feature分支,然后在使用git add,git commit 删除分支 假如你已经开发完了new-feature ,并且已经commit代码了,你就可以自由的删除这个分支了。git branch -d new-feature 如果分支没有合入master,会报下面的错误: 12error: The branch 'new feature' is not fully merged.If you are sure you want to delete it, run 'git branch -D crazy-experiment'. 这时候你就可以合并分支,如果你真的确定要删除分支,可以使用-D执行强制删除:git branch -D new-feature 切换分支(git checkout) git checkout命令允许你切换到用git branch创建的分支。切换分支会更新当前工作目录中的文件,还可以使用git log查看当前分支的历史。 123git checkout <existing-branch> //切换到一个已有分支上git checkout -b <new-branch> //创建时进行直接切换git checkout -b <new-branch> <existing-branch> //在已有的分支上创建分支,原来的分支是新分支的基 git branch和git checkout是一对兄弟,你可以使用git checkout在不同的分支或者bug分支之间切换,而不产生影响。 合并(git merge) 合并是git将被fork的历史放回到一起的方式。git merge命令允许你将git branch创建的多条分支合并成一个。 12git merge <branch> //将指定分支并入当前分支git merge --no-ff <branch> //经指定分支并入当前分支,但总是生成一个合并提交(即是快速向前合并)。这可以用来记录仓库中发生的所有合并。 一旦在分支上完成开发,我们需要把新分支的提交合并到主分支,git会根据目前分支之间的结构信息,选择不同的算法来完成合并。 快速向前合并 三路合并 快速向前合并当new-feature的分支与原有的master分支呈现线性关系时,执行快速向前合并,git将当前的HEAD指针快速移到目前分支的顶端,master分支也就拥有了new-feature分支的历史了, 来看一个快速向前合并的实例 123456789101112131415# 开始新功能git checkout -b new-branch master# 编辑文件git add <file>git commit -m '开始新功能'# 编辑文件git add <file>git commit -m '完成功能'# 合并new-feature分支git checkout mastergit merge new-featuregit branch -d new-feature 对于合作开发的人少的项目,这是一种主要的工作流,合作开发的人多的话,主master分支经常会有新提交,如果你的new-feature耗时比较久,再提交时,master分支就可能过去几个版本了,这时候就需要下面的三路合并了。 三路合并如果master分支在new-feature分离后,又有了新的提交,即开始分叉了,git只能执行三路合并,三路合并使用一个专门的提交来合并两个分支的历史。 所谓的三路也就是:两个分支的顶端以及他们共同的祖先。在执行三路合并后: 使用三路合并产生的合并提交作为两个分支的连接标志。 解决冲突如果两个分支对同一个文件的同一部分均有修改时,git将无法判断应该使用哪个,这时候合并提交会停止,需要你手动解决这些冲突。你可以使用git status来查看哪里存在冲突,也可以在目录下执行grep -rn HEAD来查看哪些文件里有这个标记,有这个的地方都是有冲突的。 当修改完所有的冲突后,git add所有的冲突文件,运行git commit生成一个合并提交,这和提交一个普通快照的流程相同。提交冲突只会存在三路合并中,快速向前合并中不可能出现针对同一文件同一部分的不一样的修改。 123456789101112131415161718192021# 开始新功能git checkout -b new-feature master# 编辑文件git add <file>git commit -m '开始新功能'# 编辑文件git add <file>git commit -m '完成功能'# 在master分支上开发git checkout master# 编辑文件git add <file>git commit -m '在master上添加了一些及其稳定的功能'# 合并new-feature分支git merge new-featuregit branch -d new-feature 这时候,merge会停止,因为无法将master直接移动到new-feature。所有需要你手动合并冲突后在提交。]]></content>
</entry>
<entry>
<title><![CDATA[websocket入门]]></title>
<url>%2F2017%2F04%2F22%2FHTML5%2Fwebsocket%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[websocket基础 websocket API使你可以通过web,在客户端应用程序和服务器端进程进行之间建立全双工通信。 websocket构造函数 为了建立到服务器的websocket连接,使用websocket接口,通过指向一个代表所要连接端点的URL,实例化一个websocket对象。websocket协议定义了两种方案—ws和wss,分别用于客户端和服务器之间的非加密和加密流量。ws(websocket)方案与http URI方案类似。wss(websocket secure)URI方案表示使用传输层安全性(TLS,也叫SSL)的websocket连接,使用https采用的安全机制来保证http连接安全。 websocket构造函数有一个必须的参数URL(指向连接目标的URL)和一个可选参数protocols(为了建立连接,服务器必须在其响应中包含的一个或一组协议名称)。在protocols参数中可以使用的协议包括xmpp(eXtensible Messaging and Presence Protocol,可拓展消息处理现场协议),soap(Simple Object Access Protocol,简单对象访问协议)或者自定义协议。 1234var echoSocket = new WebSocket('ws://echo.websocket.org',['com.kaazing.echo','example.imaginary.protocol']);echoSocket.onopen = function(){ console.log(echoSocket.protocol);} websocket事件 websocket API是纯事件驱动的。应用程序代码监听websocket对象上的事件,以便处理输入数据和连接状态的改变。websocket协议也是事件驱动的。客户端应用程序不需要轮询服务器来得到更新的数据。消息和事件将在服务器发送它们的时候异步到达 websocket事件:open 一旦服务器响应了websocket连接,open事件触发并建立一个连接。open事件对应的回调函数称为onopen。 123ws.onopen = function(){ console.log('Connection open ....');} 到open事件触发时,协议握手已经完成,websocket已经准备好发送和接收数据。如果应用程序接收到一个open事件,那么可以确定websocket服务器成功的处理了连接请求,并且同意与应用程序通信。 websocket事件:message websocket消息包含来自服务器的数据。message事件在接收到消息时触发,对应于该事件的回调函数是onmessage。除了文本,websocket消息还可以处理二进制数据,这种数据作为BLOB消息或者ArrayBuffer消息来处理。因为设置websocket消息二进制数据类型的应用程序会影响二进制消息,所以必须在读取数据之前决定用于客户端二进制输入数据的类型。 1234567891011121314151617181920212223242526ws.onmessage = function(){ if(typeof e.data ==='string'){ console.log('String message received',e,e.data); }else{ console.log('String message received',e,e.data); }} //接受Blob消息ws.binaryType = 'blob';ws.onmessage = function(){ if(e.data instanceof Blob){ console.log('Blob message received',e.data); var blob = new Blob(e.data); }} //接受ArrayBuffer消息 ws.binaryType = 'arraybuffer';ws.onmessage = function(){ if(e.data instanceof ArrayBuffer){ console.log('ArrayBuffer message received',e.data); var blob = new Uint8Array(e.data); }} websocket事件:error error事件在响应意外故障的时候触发。与该事件对应的回调函数为onerror。错误还会导致websocket连接关闭。如果你接受一个error事件,可以预期很快就会触发close事件。 1234ws.onerror = function(e){ console.log('error',e); handle(e);} websocket事件:close close事件在websocket连接关闭时触发。对应于close事件的回调函数是onclose。一旦连接关闭,客户端与服务器不再接受或者发送消息。 123ws.onclose = function(e){ console.log('close',e);} close事件有三个有用的属性(property),可以用于错误处理和恢复:wasClean,code和error。wasClean属性是一个布尔属性,表示连接是否顺利关闭连接。如果websocket的关闭是对来自服务器的close帧的响应,则该属性为true。如果连接因为其它原因(列如,因为底层TCP连接关闭),则该属性为false。code和reason属性表示服务器发送的关闭握手状态。这些属性和websocket.close()方法中的code和reason参数一致。 websocket方法:send()方法和close()方法 send()方法 使用websocket在客户端和服务器之间建立双全工双向连接后,就可以在连接打开时(在调用onopen监听器之后,调用onclose监听器之前)调用send方法。使用send方法可以从客户端向服务器发送消息。在发送一条或者多条消息之后,可以保持连接打开,或者调用close方法关闭。ws.send(‘hello’) 1234var ws = new WebSocket('ws://echo.websocket.org');ws.onopen = function(){ ws.send('hello');} close()方法 使用close方法,可以关闭websocket连接或者终止连接尝试。如果连接已经关闭,该方法什么也不做。ws.close()可以向close方法传递两个可选参数:code(数字型的状态代码)和reason(一个文本字符串)。传递这些参数能够向服务器传递关于客户连接原因的信息。 websocket对象特性 readyState: websocket对象通过只读属性readyState报告其连接状态。 特性常量 取值 状态 WebSocket.CONNECTING 0 连接正在进行中,但还未建立 WebSocket.OPEN 1 连接已经建立。消息可以在客户端和服务器之间通信 WebSocket.CLOSING 2 连接正在进行关闭握手 WebSocket.CLOSED 3 连接已经关闭,不能打开 bufferedAmount: 设计应用程序时,你可能想要检查发往服务器的缓存数据量,特别是在客户端应用程序向服务器发送大量数据时。尽管调用send是立即生效的,但是数据在互联网上的传输却不是如此。浏览器将为你的客户端应用程序缓存出站数据,从而使你可以随时调用send(),发送任意数量的数据。你可以使用bufferedAmount特性检查已经进入队列,但是尚未发送到服务器的字节数。这个特性报告的值不包括协议组帧开销或者操作系统,网络硬件所进行的的缓存。 123456789var THRESHOLD = 10240;var ws = new WebSocket('ws://echo.websocket.org/updates');ws.onopen = function(){ setInterval(function(){ if(ws.bufferedAmount<THRESHOLD){ ws.send(getApplicationState()); } },1000);} protocol 在前面关于websocket构造函数的讨论中,我们提到了protocol参数,它让服务器知道客户端理解并可在websocket上使用的协议。websocket对象的protocol特性提供了另一条关于websocket实例的有用信息。客户端和服务器协议协商的结果可以在websocket对象上看到。protocol特性包含在打开握手期间websocket服务器选择的协议名,换句话说,protocol特性告诉你特定websocket上使用的协议。protocol特性在最初的握手完成之前为空,如果服务器没有选择客户端提供的某个协议,该特性保持控制。 完整客户端代码如下:123456789101112131415161718192021222324252627282930313233343536373839404142<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Websocket Echo Client</title></head><body> <div id="output"></div> <script type="text/javascript"> function setup(){ output = document.getElementById('output'); ws = new Websocket('ws://echo.websocket.org/echo'); function log(s){ var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.textContent = s; output.appendChild(p); console.log(s); } function sendMessage(msg){ ws.send(msg); log('Message send'); } ws.onopen = function(e){ log('Connected'); sendMessage('Hello Websocket'); } ws.onclose = function(e){ log('Disconnected:'+e.reason); } ws.onerror = function(e){ log('Error '); } ws.onmessage = function(e){ log('Message received'+e.data); ws.close(); } } setup(); </script></body></html> 结合websocket使用HTML5媒体的完整客户端应用程序1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Drop Image Here</title></head><body> <script type="text/javascript"> var ws = new Websocket('ws://echo.websocket.org/echo'); ws.onopen = function(){ console.log('open'); } //处理二进制数据 ws.onmessage = function(e){ var blob = e.data; console.log('message :'+blob.size +'bytes'); if(window.webkitURL){ URL = webkitURL; } var uri = URL.createObjectURL(blob); var img = document.createElement('img'); img.src = uri; document.body.appendChild(img); } document.ondrop = function(e){ document.body.style.backgroundColor = '#fff'; try{ e.preventDefault(); handleFileDrop(e.dataTransfer.files[0]); return false; }catch(e){ console.log(e); } } document.ondragover = function(e){ e.preventDefault(); document.body.style.backgroundColor = '#6fff41'; } document.ondragleave = function(e){ e.preventDefault(); document.body.style.backgroundColor = '#fff'; } function handleFileDrop(file){ var reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = function(){ console.log('sending: '+file.name); ws.send(reader.result); } } </script></body></html>]]></content>
</entry>
<entry>
<title><![CDATA[jquery异步获取数据]]></title>
<url>%2F2017%2F04%2F20%2Fjquery%2Fjquery%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE%2F</url>
<content type="text"><![CDATA[jquery异步获取数据 jquery异步获取数据 使用load()方法异步请求数据 使用load()方法通过ajax请求加载服务器的数据,并把返回的数据放置到指定的元素中。load(url,[data],[callback]),参数URL为加载服务器地址,可选项data参数为请求时发送的数据,callback参数为数据请求成功后执行的回调函数。 123456789$(function(){ $('#btn').on('click',function(){ var this = $(this); $('.content').html('<img src='loading.gif'/>') .load('fruit.html',function(){ $this.attr('disabled',true); }); });}); 使用getJSON()方法异步加载json格式数据 使用getJSON()方法可以通过ajax异步请求的方式获取服务器中的数据,并对获取到的数据进行解析,显示在页面中。$.getJSON(url,[data],[callback]),其中,URL参数为请求加载json格式文件的服务器地址,可选项data参数为请求时发送的数据,callback参数为数据请求成功后,执行的回调函数。 123456789$('#btn').on('click',function(){ var this = $(this); $.getJSON('sport.json',function(data){ this.attr('disabled',true); $.each(data,function(index,sport){ $('ul').append(`<li>sport["name"]</li>`); }); }); }) 使用getScript()方法异步加载并执行js文件,$.getScript(url,[callback]) 使用get()方法以GET方式从服务器获取数据,通过方法中中的回调函数的参数返回请求的数据。 12345678$('#btn').on('click',function(){ var this = $(this); $.get('info.php',function(data){ this.attr('disabled',false); $('ul').append(data.name); $('ul').append(data.say); });}); 使用post方法以post方式向服务器发送数据 post()方法多用于以post方式向服务器发送数据,服务器接受到数据后,进行处理,并将处理结果返回给页面,$.post(url,[data],[callback]),参数URL为加载服务器地址,可选项data参数为请求时发送的数据,callback参数为数据请求成功后执行的回调函数。 12345678910$(function(){ $('#btn').on('click',function(){ $.post('check.php',{ num: $('#number').val() }, function(data){ $('ul').append(`<li>你输入的${$('#number'),val()}是<b>${data}</b></li>`); }); });}); 后台代码如下: 123456$num = $_POST['num'];if($num%2==0){ echo '偶数';}else{ echo '奇数';} 使用serialize()方法序列化表单元素值 使用serialize()方法可以将表单中有name属性的元素值进行序列化,生成标准的URL编码文本字符串,直接用于ajax请求,调用格式如下:$(selector).serialize() 使用ajax方法加载服务器数据 ajax方法是最底层,功能最强大的请求服务器数据的方式,它不仅仅可以获取服务器返回的数据,还可以向服务器发送并传递数值。$.ajas([settings]),其中参数setting为发送ajax请求时的配置对象,在该对像中,dada为请求时传递的数据,dataType为服务器返回的数据类型,success为请求成功时的回调函数,type为发送数据请求的方式,默认为get。 12345678910111213$(function(){ $('#btn').on('click',function(){ var this = $(this); $.ajax({ url: 'article.php', dataType: 'text', success: function(data){ this.attr('disabled',false); $('ul').append(data); } }); });}); 使用ajaxSetup方法设置全局ajax默认选项 使用ajaxSetup()方法可以设置,ajax请求的一些全局变量,设置完成后,后面的ajax请求不需要添加这些选项值。$.ajaxSetup([options]) 123456789101112131415161718192021$(function(){ $.ajaxSetup({ dataType: 'text', success: function(data){ $('ul').empty().append(data); } });});$('#btn1').on('click',function(){ $.ajax({ url: 'a1.txt' });}); $('#btn2').on('click',function(){ $.ajax({ url: 'a2.txt' });}); 使用ajaxStart()和ajaxStop()方法 两者绑定ajax时间。ajaxStart()方法用于在ajax请求发出前触发函数,ajaxStop()方法在ajax请求完成后触发函数。]]></content>
</entry>
<entry>
<title><![CDATA[commonJS,AMD与CMD的区别与联系]]></title>
<url>%2F2017%2F04%2F16%2FJavaScript%2FcommonJs_AMD_CMD%E6%AF%94%E8%BE%83%2F</url>
<content type="text"><![CDATA[commonJS,AMD与CMD的区别与联系 1. CommonJS原本叫serverJS,是以在浏览器环境之外构建JavaScript生态环境为目标而产生的项目,比如在服务器和桌面环境中。 commonJS规范是为了解决JavaScript的作用于问题而定义的模块形式,可以使每个模块在它自身的命名空间中执行。 该规范的主要内容是,模块必须通过module.exports导出对外的变量或接口,通过require()来导入其它模块的输出到当前模块作用域中。Node.js就是采用commonJS规范。 12345678//moduleA.jsmodule.exports = function(){ console.log('hello');}//moduleB.jsvar moduleA = require('./moduleA');moduleA(); 2.AMD(Asynchronous Module Definition)(异步模块定义)是为了浏览器环境设计的,因为CommonJS模块系统是同步加载的,当前浏览器环境是异步的。 AMD定义了一套JavaScript模块依赖异步加载标准,来解决同步加载问题。 模块通过define函数定义在闭包中,格式如下 define(id? String, dependencies? String[], factory: Function|Object) id是模块名字,它是可选参数 dependencies 指定了所要依赖的模块列表,他是一个数组,也是可选的参数,每个依赖的模块的输出将作为参数一次传入factory中。如果没有指定dependencies,那么他的默认值是[‘require’,’exports’,’module’]。 factory 是最后一个参数,它包裹了模块的具体实现,他是一个函数或是一个对象。如果是函数,那么它的返回值就是模块的的输出接口或值。 定义一个moduleA,它依赖jQuery模块 123456define('moduleA',['jquery'],function($){ //$是jquery模块输出 $('body').text('hello');});//使用define(['moduleA'],function(moduleA){}); require.js就是采用AMD模块 3.CMD(Common Module Defination) 在cmd规范中,一个模块就是一个文件。代码的书写格式如下:define(factory)define是全局函数,用来定义模块。define接受factory参数,factory可以是函数,对象或字符串。 当factory为对象或字符串时,表示该模块的接口就是该对象或字符串,比如要定义一个json数据模块define({‘key’,’value’}) 也可以通过字符串定义模板模块 define(‘Hello ‘) factory是函数时,表示是模块的构造方法。执行该构造方法,可以得到模块对外提供的接口,factory方法在执行时,默认会传入三个参数,require,exports,module 123define(function(require,exports,module){ //需要导出的模块}); require是方法,接收模板标识作为唯一参数,用来获取其它模块提供的接口 1234define(function(require, exports){ const a = require('./a'); a.doSomething();}); require是同步往下执行的,require.async(id,callback?)用来在模块内部异步加载模块,并在加载完后执行回调函数。 123456define(function(require, exports){ require.async(['./a,'./b], function(c ,d ){ c.doSomething(); d.doSomething(); });}); factory是函数时的第二个参数是exports,exports是一个对象,用来对外提供模块接口 123456define(function(require, exports){ exports.str = 'xxxxxxxxx'; exports.doSomething = function(){ //对外提供doSomething方法 }}); 除了exports外,还可以用return直接对外提供对象接口 12345678define(function(require, exports){ return{ str: 'xxxxxxx', doSomething: function(){ } }}); exports是module.exports对象的一个引用,很多时候exports都无法满足需求,列如对外提供一个实例对象 12345678define(function(require, exports,module){ function Person(name, age){ this.name = name; this.age = age; } module.exports = new Person('lily',34);}); AMD与CMD比较AMD默认是依赖前置,在一开始就将需要依赖的文件配置并加载好 1234define('moduleA',['jquery'],function($){ //依赖的配置文件已经配置并加载好了 $('body').text('hello world'); }); CMD是依赖就近,需要使用的时候才会去配置加载 123456define(function(require, exports){ var a = require('./a.js'); //配置并加载,同步 if(false){ var b = require('./b.js'); //配置的文件永远不会被加载 }});]]></content>
</entry>
<entry>
<title><![CDATA[phantomJS简单使用方法]]></title>
<url>%2F2017%2F04%2F14%2Fnodejs%2FPhantomJS%2F</url>
<content type="text"><![CDATA[phantomJS简单使用方法 概述有时,我们需要浏览器处理网页,但并不需要浏览,比如生成网页的截图,抓取网页数据等操作。PhantomJS的功能,就是提供一个浏览器环境的命令行接口,你可以把它看做一个“虚拟浏览器”,除了不能浏览,其它与正常浏览器一样。它的内核是webkit引擎,不提供图形界面,只能在命令行中使用。 webpage模块webpage模块是PhantomJS的核心模块,用于网页操作。 12var webpage = require('webpage');var page = webpage.create(); 上面代码表示加载PhantomJS的webpage模块,并创建一个实例。 open()方法 该方法用于打开具体的网页 12345var page = require('webpage').create();page.open(url,function(s){ console.log(s); phantom.exit(); }); 上述代码中,open()方法,用于打开具体的网页。他接受两个参数。第一个参数是网页的网址,第二个参数是回调函数,网页打开后该函数将会运行,它的参数是一个表示状态的字符串,如果打开成功就是success,否则就是fail。 只要接受到服务器返回的结果,PhantomJS就会报告网页打开成功,而不管服务器是否返回404或者500错误。 open方法默认使用GET方法,与服务器通信,但是也可以使用其它方法。 123456var webpage = require('webpage');var page = webpage.create();var postbody = 'user=user&password=password';page.open(url,'POST',postbody,function(status){ console.log('Status: '+status);}); 上面代码中,使用post方法向服务器发送数据。open方法的第二个参数来指定http方法,第三个参数用来指定该方法所要使用的数据。 open方法还允许提供配置对象,对HTTP请求进行更详细的配置。 1234567891011121314151617var webpage = require('webpage');var page = webpage.create();var settings = { operation: 'POST', encoding: 'utf-8', headers: { 'Content-Type': 'application/json' }, data: JSON.stringfy({ some: 'data', another: ['customer','data'] });};page.open(url,settings,function(status){ console.log('Status'+status);}) evalute()方法 evalute方法用于打开网页后,在页面中执行JavaScript代码。 123456789var page = require('webpage').create();page.open(url,function(status){ var title = page.evalute(function(){ return document.title; }); console.log('Page title is'+ title); phantom.exit(); }); 网页内部的console语句,以及evalute方法内部的console语句,默认不会显示在命令行。这时,可以用onConsoleMessage方法监听这个事件,进行处理。 12345678910var page = require('webpage').create();page.onConsoleMessage = function(msg) { console.log('Page title is ' + msg);};page.open(url, function(status) { page.evaluate(function() { console.log(document.title); }); phantom.exit();}); includeJs() includeJs方法用于页面加载外部脚本,加载结束后就调用指定的回调函数。 123456789var page = require('webpage').create();page.open(url,function(){ page.includeJs('jquery.min.js',function(){ page.evalute(function(){ $('button').click(); }); }); phantom.exit();}); 上面的例子在页面中注入jquery脚本,然后点击所有的按钮。需要注意的是,由于是异步加载,所以Phantom.exit()语句要放在page.includeJs()方法的回调函数之中,否则页面会过早移除。 render()方法 render方法用于将网页保存成图片,参数就是指定的文件名。该方法根据后缀名,将网页保存成不同的格式,目前支持PNG,GIF,JPEG和PDF。 1234567var webpage = require('webpage');var page = webpage.create();page.viewportSize = {width: 1920, height: 1080};page.open(url,function(){ page.render('main.jpeg',{format: 'jpeg',quality: '100'}); phantom.exit();}); 该方法还可以接受一个配置对象,format字段用于指定图片格式,quality字段用于指定图片质量,最小为0,最大为100。 viewportSize,zoomFactor viewportSize属性指定浏览器视口的大小,即网页加载的初始浏览器窗口大小。 123456var webpage = require('webpage');var page = webpage.create();page.viewportSize = { width: 480, height: 800}; viewportSize的height字段必须指定,不可省略。 zoomFactor属性用来指定渲染时页面的放大系数,默认是1。 1234var webpage = require('webpage');var page = webpage.create();page.zoomFactor = 0.25;page.render('capture.png');]]></content>
</entry>
<entry>
<title><![CDATA[爬取网易云音乐]]></title>
<url>%2F2017%2F04%2F14%2Fnodejs%2F%E7%88%AC%E5%8F%96%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%2F</url>
<content type="text"><![CDATA[爬取网易云音乐 该爬虫使用PhantomJS,cheerio,bluebird以及Node.js内置模块来实现 首先,我们需要认识到利用普通爬取静态网页的方式是爬取不到源码的,你只会拿到源码的模板,数据根本没有被填充,所以我们需要使用PhantomJS这个工具。它的功能,就是提供一个浏览器环境的命令行接口,你可以把它看做一个虚拟的浏览器,除了不能浏览,其它与正常浏览器一样。它的内核是wenkit引擎,不提供图形界面,只能在命令行下使用。其次,我们使用cheerio模块,其提供了类似于jquery的方法来解析爬取到的HTML代码,另外,我们使用blueBird来异步爬取。(这些模块可以使用npm来下载安装) 官网下载到的phantomJS是一个可执行程序,下载完成后配置其环境变量。然后利用Node.js的child_process新起进程来调用此命令。具体代码如下: 123456789101112131415161718192021//1.js文件var page = require('webpage').create();page.onConsoleMessage = function(msg) { console.log('Page title is ' + msg);};page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";page.open('url', function(status) {//open方法的第一个参数是需要爬去的网址 if (status !== 'success') { console.log('Unable to access network'); }else{ console.log('正在爬取请稍后'); setTimeout(function() { var result = page.evaluate(function() { return document.getElementById("g_iframe").contentDocument.documentElement.innerHTML; }); console.log(result); phantom.exit(); }, 7000); //需进行延迟等待,带页面渲染完毕再进行爬取 }}); 主程序代码如下:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677var cp = require('child_process');var http = require('http');var cheerio = require('cheerio');var Promise = require("bluebird");var fs = require('fs');var ids = []; //存取歌曲IDvar list=[]; //存取所有promise对象var details = []; //存取歌曲信息var url = 'http://music.163.com/api/song/detail/?id=425137664&ids=[425137664]&csrf_token=';var urls = []; //存取歌曲json文件信息function getJson(u){ return new Promise(function(resolve,reject){ http.get(u, function(res) { var html = ""; res.on("data", function(data) { html += data; }); res.on("end", function() { resolve(html); }); }).on("error", function(e){ reject(e); console.log("获取信息出错!"); }); });}/** * 解析网页 */function parseContent($){ var tr = $('tbody').find('tr'); tr.each(function(i,t){ var td = $(t).find('td').eq(1); var id = $(td).find('a').attr('href').split('=')[1]; ids.push(id); //获取到歌曲id }); //拼接URL,获取到歌曲的json文件 ids.forEach(function(num){ var u = url.replace(/\d{4,}/g,num); urls.push(u); }); urls.forEach(function(url){ list.push(getJson(url)); }); Promise.all(list).then(function(jsons){//解析获取到的json文件 jsons.forEach(function(json){ var json = JSON.parse(json); //获取到歌曲的基本信息 var name = json.songs[0].name; var singer = json.songs[0].artists[0].name; var picUrl = json.songs[0].album.picUrl; var src = json.songs[0].mp3Url; details.push({ "name": name, "singer":singer, "picUrl":picUrl, "src" : src }); }); fs.writeFile('music.json',JSON.stringify(details)); //将获取到的数据以json格式写入到文件中 });}//新建进程利用phantomJS来运行1.js文件cp.exec('phantomjs.exe 1.js',{ timeout: 20000000, //超时时间},function(error,stdout,stderr){ //传给回调的 stdout 和 stderr 参数会包含子进程的 stdout 和 stderr 的输出 if(error){ console.error(`exec error ${error}`); return; } let $ = cheerio.load(stdout); //利用cheerio来解析爬取到的HTML parseContent($);}); 然后,你就可以在music.json中看到自己需要爬去的歌曲信息了 。]]></content>
</entry>
<entry>
<title><![CDATA[页面置换算法的实现]]></title>
<url>%2F2017%2F04%2F13%2FOS%2F%E9%A1%B5%E9%9D%A2%E7%BD%AE%E6%8D%A2%E7%AE%97%E6%B3%95%2F</url>
<content type="text"><![CDATA[页面置换算法的实现 页面置换算法的实现在进程运行过程中,若其所要访问的页面不在内存,而需把它们调入内存,但内存已无空闲空间时,但为了保证该进程能正常运行,系统必须从内存中调出一页程序或数据送到磁盘的对换区中。但应将哪个页面调出。需根据一定的算法来确定。通常,把选择换出页面的算法称为页面置换算法。 一个好的页面置换算法应具有较低的页面置换频率。从理论上上讲,应将那些以后不再会访问的页面换出,或把那些在较长时间内不会再访问的页面换出。 常用算法介绍 最佳置换算法 最佳置换算法是由Belady与1966年提出的一种理论上的算法。其所选择的被淘汰页面将是以后永不使用的,或许是在最长时间内不再被访问的页面。采用最佳置换算法通常可保证获得最低的缺页率。但由于人们目前还无法预知,一个进程在内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的。因而该算法是无法实现的。 先进先出页面置换算法 FIFO算法是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面按先后次序连接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量,常用函数,例程等的页面,FIFO算法并不能保证这些页面不被淘汰。 最近最久未使用算法 最近最久未使用(LRU)的页面置换算法是根据页面调入内存后的使用情况作出决策的。由于无法预测各页面将来的使用情况,只能利用”最近的过去”作为”最近的将来”的近似,因此,LRU置换算法是选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间t。当需淘汰一个页面时,选择现有页面中其t值最大的,即最近最久未使用的页面予以淘汰。 LRU置换算法的硬件支持 寄存器为了记录某进程在内存中各页的使用情况,须为每个在内存中的页面配置一个移位寄存器,可表示为:R=Rn-1Rn-2Rn-3·····R1R0当某进程访问某物理块时,要将相应寄存器的Rn-1位置成1。此时,定时信号将每隔一定时间(列如 100ms)将寄存器右移一位。如果我们把n位寄存器的数看做一个整数,那么,具有最小数值的寄存器所对应的页面,就是最久未使用的页面。 栈可利用一个特殊的栈来保存当前使用的各个页面的页面号。每当进程访问页面时,便将该页面的页面号从栈中移除,将它压入栈顶。因此,栈顶始终是最新被访问的页面的编号,而栈底则是最近最久未使用页面的编号。 FIFO与LRU使用栈数据结构代码效果如下(源码可按F12查看):]]></content>
</entry>
<entry>
<title><![CDATA[jquery基本事件]]></title>
<url>%2F2017%2F04%2F08%2Fjquery%2Fjquery%E5%9F%BA%E6%9C%AC%E4%BA%8B%E4%BB%B6%2F</url>
<content type="text"><![CDATA[jquery基本事件 jquery基本事件 页面加载时触发ready()事件 ready()方法类似于onload()事件,但前者只要页面的dom结构加载后触发,而后者需要页面全部元素加载成功才触发,ready可以写多个,其按顺序执行。$(document).ready(function(){$('#tip').html('加载完毕');});。 使用hover方法切换事件 hover方法的功能是当鼠标移到所选元素上时,执行方法中的第一个函数,鼠标移出时,执行方法中的第二个函数,实现事件的切换效果。$(selector).hover(over,out)。 使用toggle()方法绑定多个函数 toggle()方法可以在元素的click事件中绑定两个或两个以上的函数,同时,它还可以实现元素的隐藏与显示的切换,绑定多个函数的调用格式$(selector).toggle(fun1,fun2,······funN)。 使用unbind()方法移除移除元素绑定事件 $(selector).unbind(event,fun),其中参数event表示需要移除的事件名称,多个事件用空格隔开,fun参数为事件执行时调用的函数名称,如果没有规定参数,unbind()方法会删除指定元素的所有事件处理程序。 使用one()方法绑定元素的一次性事件 one()方法可以绑定元素任何有效的事件,但这种方法的绑定事件只会触发一次。$(selector).one(event,[data],fun),参数event为事件名称,dada为触发事件时携带的数据,fun为触发该事件时执行的函数。 调用trigger方法手动触发指定事件。 trigger方法可以直接手动触发元素指定的事件,这些事件可以是元素自带事件,也可以是自定义事件。总之,该事件必须能执行。 文本框的focus和blur事件 focus事件在元素获取焦点时触发,如点击文本框时触发该事件;而blur事件则在元素失去焦点时触发。 下拉列表框的change事件 当一个元素的值发生变化时,将会触发change事件。 调用live()方法绑定事件 与bind相同,live可以绑定元素的可执行事件,除此之外,live方法还可以绑定动态元素,即是使用代码添加的元素事件。 调用show()和hide()方法显示和隐藏元素 $(selector).hide(speed,[callback])和$(selector).show(speed,[callback]),参数speed设置隐藏或显示的速度值,可为’slow’,’fast’或毫秒数值。可选参数callback为隐藏或显示动作执行完成后调用的函数。 使用slideUp和slideDown方法的滑动效果 前者用于向上滑动元素,后者用于向下滑动元素,调用方法为$(selector).slideUp(speed,[callback])和$(selector).slideDown(speed,[callback]),其中speed为滑动时的速度,单位是毫秒,可选参数是滑动成功后的回调函数。 使用slideToggle方法 该方法可以切换slideUp和slideDown,即调用该方法时,如果元素已向上滑动,则元素自动向下滑动,反之,向下滑动。$(selector).slideToggle(speed,[callback])。 使用fadeIn()和fadeOut()实现淡入与淡出 $(selector).fadeIn(speed,[callback])和$(selector).fadeOut(speed,[callback])。 使用fadeTo()方法设置淡入淡出效果的不透明度 调用fadeTo()方法,可以将所选元素的不透明度以淡入淡出的效果调整为指定的值。$(selector).fadeTo(speed,opacity,[callback]),其中speed参数为效果的速度,opacity参数为指定的不透明度,取值为0~1,可选参数callback为效果完成后回调的函数。 调用animate()方法制作简单的动画效果 格式为$(selector).animate({params},speed,[callback])。其中,params参数为制作动画效果的css属性名与值,speed参数为动画的效果速度,单位为毫秒,可选项callback参数为动画效果完成后执行的回调函数。$('img').animate({left: '+=90px',3000,function(){$(this).animate({width:'+=30px',height:'+=30px'},3000,function(){$('#tip').html('done!');});});。 调用stop方法停止当前动画效果 stop()方法的功能是在动画完成前,停止当前正在执行的动画效果,这些效果包括滑动,淡入淡出和自定义动画。$(selector).stop([clearQueue],[goToEnd])。 调用delay()方法延迟执行动画效果 $(selector).delay(duration),其中duration为延时值,单位是毫秒,当超过延时值时,动画开始执行。]]></content>
</entry>
<entry>
<title><![CDATA[jquery基本方法]]></title>
<url>%2F2017%2F04%2F08%2Fjquery%2Fjquery%E5%9F%BA%E6%9C%AC%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[jquery基本方法 jquery基本方法 使用attr()方法来控制元素的属性 attr()方法的作用是设置或者返回元素的属性,其中attr(属性名)格式是获取元素属性的值,attr(属性名,属性值)是设置元素属性的值。 操作元素的内容 使用html()和text()方法操作元素的内容,当两个方法的参数为空时,表示获取该元素的内容。而如果方法中包含参数,则表示将参数值设置为元素内容。html()方法可以获取元素的HTML文本。因此,原文中的格式代码也被一起获取,而text()方法只是获取元素中的文本内容,并不包含HTML格式代码。 操作元素内容 通过addClass()和css()方法可以方便的操作元素的样式,前者括号中的参数为增加元素的样式名称,后者直接将样式内容写入到括号里。使用removeAttr(name)和removeClass(class)分别可以实现移除元素的属性和样式的功能,前者方法中的参数表示移除属性名,后者方法中的参数则表示样式名。 使用append()方法向元素内追加内容 append(content)方法的功能是向指定的元素内追加内容,被追加的content参数可以表示字符,HTML标记,还可以是一个返回字符串内容的函数。 使用appendTo()方法向被选元素内插入内容 $(content).appendTo(selector),参数content表示要插入的内容,参数select表示被选的元素,即把content内容插入到selector元素内,默认在尾部。 使用before()和after()在元素前后插入内容 $(selector).before(content)和$(selector).after(content)。 使用clone()方法复制元素 调用clone()方法可以生成一个被选元素的副本,即复制了一个被选元素,包含它的节点,文本,属性。 $(selector).clone()。 replaceWith()和replaceAll()替换内容 它们在使用时,内容和被替换元素所在的位置不同,$(selector).replaceWith(content),$(content).replaceAll(selector)。 使用wrap()和wrapInner()方法包裹元素的内容 前者用于包裹元素本身,后者用于包裹元素的内容,调用格式为$(selector).wrap(wrapper),$(selector).wrapInner(wrapper)。 使用each方法遍历元素 使用时,通过回调函数返回遍历元素的序列号。$('span').each(function(index){if(index==2){$(this).addClass('focus');}});。 使用remove()和empty()方法删除元素 remove()方法删除所选元素本身和子元素,该方法可以添加过滤参数指定要删除的某些元素,而empty()方法则只删除所选元素的子元素。]]></content>
</entry>
<entry>
<title><![CDATA[jquery选择器]]></title>
<url>%2F2017%2F04%2F08%2Fjquery%2Fjquery%E9%80%89%E6%8B%A9%E5%99%A8%2F</url>
<content type="text"><![CDATA[jquery选择器使用 jquery选择器 #id选择器 jquery能够使用css选择器来操作网页中的标签元素。如果你想要通过一个ID号去查找一个元素,$('#my_id'),其中#my_id表示根据ID选择器获取页面中的指定标签元素,且返回唯一一个元素。 element选择器 其中element就是元素的名称 .class选择器 $('.class') 其中, .class参数表示元素的css类别(类选择器名称) sele1,sele2,…,seleN选择器 有时需要精确的选择任意多个指定的元素。$('sele1,sele2,...seleN') ,其中sele1,sele2到seleN为有效选择器,每个选择器之间用逗号来隔开。它们可以是之前提及的各种类型选择器,如$('#id'),$('.class'),$('selector') 选择器等。 ance , desc 选择器 $('ance desc'),其中ance,desc是使用空格隔开的两个参数,ance表示父元素,desc表示后代元素,包括子元素,孙元素等。两个元素都可以通过选择器来获取。 parent > child $('parent > child'),它所选择的目标是子集元素,相当于一个家庭中的子辈们,但不包括孙辈。 prev + next 选择器 $('prev + next'),查找与”prev”元素紧邻的下一个”next”元素,并且只返回唯一一个元素。 prev ~ siblings选择器 $('prev ~ siblings') 获取prev元素后边全部紧邻的元素。 :first 过滤选择器 $('li:first') 得到一组相同标签中的第一个元素。 :eq(index)过滤选择器$('li:eq(3)')在一组标签元素数组中,灵活选择任意一个标签元素。 :contains(text)过滤选择器希望按照文本内容来查找一个或多个元素,功能是选择包含指定字符串的全部元素。 :has(selector)过滤选择器$('li:has('p')')是获取选择器中包含指定元素名称的全部字符。 :hidden过滤选择器获取全部不可见的元素,这些不可见的元素包括type属性值为hidden的元素,如$('p:hidden')。 :visible过滤选择器获取全部的可见元素,也就是说,只要不将元素的display属性值设置为none,那么都可以获取该标签。 [attribute]包含属性选择器用于选择包含给定属性的所有元素 [attribute=value]属性等于选择器获取与属性名和属性值完全相同的全部元素,其中[]是专用于属性选择器的括号符。 [attribute!=’value’]属性不等于选择器获取不包含属性名,或者与属性名和属性值不相同的全部元素 [attribute|=’value’]属性选择器获取指定属性值等于给定字符串或以字符串为前缀(该字符串后跟一个连字符‘-’)的元素 [attribute*=’value’]属性包含过滤器用于选择指定属性值包含给定字符串的所有元素 [attribute~=’value’]属性包含单词过滤器用于选择指定属性值中包含给定单词(由空格分隔)的元素 [attribute^=’value’]属性开始过滤器用于选择给定属性是以特定值开始的所有元素 [attribute$=’value’]属性结尾过滤器用于选择给定属性是以某特定值结尾的所有元素 :first-child 获取属于其父元素和所有兄弟元素的第一个元素 :last-child 获取每个父元素中返回的最后一个子元素 :img图像域选择器当一个input元素的type属性值设为”image”时,该元素就是一个图像域,```$(‘#form :img’) :button表单按钮选择器获取’type’值为button的input和button这两类普通元素 :checked选择状态选择器(复选框,单选按钮)获取处于选重状态的全部元素 :select 选中状态选择器只能获取select下拉列表框中全部处于选中状态的option选项元素。]]></content>
</entry>
<entry>
<title><![CDATA[canvas学习第三章]]></title>
<url>%2F2017%2F04%2F08%2Fcanvas%2Fcanvas%E5%AD%A6%E4%B9%A0%E7%AC%AC%E4%B8%89%E7%AB%A0%2F</url>
<content type="text"><![CDATA[canvas学习第三章 基础知识 canvas文本API ctx.fillText([text],[x],[y],[width]) text: 在画布上渲染的文本 x: 文本在画布上的x坐标 y: 文本在画布上的y坐标 width: 在画布上渲染文本的最大宽度 ctx.font 设置所选用字体大小,榜重,样式,字体外观 font style 字体样式 font weight 字体榜重 font size 字体大小 font face 字体外观 ctx.strokeText([text],[x],[y],[maxwidth]) ctx.measureText([text]) 获得文字的宽 canvas图像API ctx.drawIamge(image,dx,dy) image 图像对象 dx ,dy 定义画布上图像在左上角的位置 ctx.drawImage(image,dx,dy,dw,dh) dw和dh代表在画布上绘制图像的矩形部分的宽和高 ctx.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh) sx,sy代表在画布上开始复制源图像的“原位置” sw,sh代表从(sx,sy)出开始的矩形宽度与高度。 综合代码如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>CH3EX3: Text Arranger 3.0</title><script src="modernizr-1.6.min.js"></script><script type="text/javascript" src="jscolor/jscolor.js"></script><script type="text/javascript">window.addEventListener("load", eventWindowLoaded, false); function eventWindowLoaded() { canvasApp();}function canvasSupport () { return Modernizr.canvas;}function eventWindowLoaded() { var pattern = new Image(); pattern.src = "texture.jpg"; pattern.onload = eventAssetsLoaded;}function eventAssetsLoaded() { canvasApp();}function canvasApp() { var message = "your text"; //输入的文本 var fontSize = "50"; //字体大小 var fontFace = "serif"; //字体外观 var textFillColor ="#ff0000"; //填充字体颜色 var textAlpha =1; //透明度 var shadowX = 1; //阴影x轴偏移 var shadowY = 1; //阴影y轴偏移 var shadowBlur = 1; //模糊度 var shadowColor = "#707070"; //阴影颜色 var textBaseline = "middle"; //文字基线 var textAlign = "center"; //文字对齐方式 var fillOrStroke ="fill"; var fontWeight ="normal"; //字体榜重 var fontStyle = "normal"; var fillType = "colorFill"; var textFillColor2 ="#000000"; var pattern = new Image(); if (!canvasSupport()) { return; } var theCanvas = document.getElementById("canvasOne"); var context = theCanvas.getContext("2d"); var formElement = document.getElementById("textBox"); formElement.addEventListener("keyup", textBoxChanged, false);//监听文本框 formElement = document.getElementById("fillOrStroke"); formElement.addEventListener("change", fillOrStrokeChanged, false); formElement = document.getElementById("textSize"); formElement.addEventListener("change", textSizeChanged, false); formElement = document.getElementById("textFillColor"); formElement.addEventListener("change", textFillColorChanged, false); formElement = document.getElementById("textFont"); formElement.addEventListener("change", textFontChanged, false); formElement = document.getElementById("textBaseline"); formElement.addEventListener("change", textBaselineChanged, false); formElement = document.getElementById("textAlign"); formElement.addEventListener("change", textAlignChanged, false); formElement = document.getElementById("fontWeight"); formElement.addEventListener("change", fontWeightChanged, false); formElement = document.getElementById("fontStyle"); formElement.addEventListener("change", fontStyleChanged, false); formElement = document.getElementById("shadowX"); formElement.addEventListener("change", shadowXChanged, false); formElement = document.getElementById("shadowY"); formElement.addEventListener("change", shadowYChanged, false); formElement = document.getElementById("shadowBlur"); formElement.addEventListener("change", shadowBlurChanged, false); formElement = document.getElementById("shadowColor"); formElement.addEventListener("change", shadowColorChanged, false); formElement = document.getElementById("textAlpha"); formElement.addEventListener("change", textAlphaChanged, false); formElement = document.getElementById("textFillColor2"); formElement.addEventListener("change", textFillColor2Changed, false); formElement = document.getElementById("fillType"); formElement.addEventListener("change", fillTypeChanged, false); formElement = document.getElementById("canvasWidth"); formElement.addEventListener("change", canvasWidthChanged, false); formElement = document.getElementById("canvasHeight"); formElement.addEventListener("change", canvasHeightChanged, false); formElement = document.getElementById("canvasStyleWidth"); formElement.addEventListener("change", canvasStyleSizeChanged, false); formElement = document.getElementById("canvasStyleHeight"); formElement.addEventListener("change", canvasStyleSizeChanged, false); formElement = document.getElementById("createImageData"); formElement.addEventListener("click", createImageDataPressed, false); pattern.src = "texture.jpg"; drawScreen(); function drawScreen() { //Background context.globalAlpha = 1; //全局透明度 context.shadowColor = "#707070"; //阴影颜色 context.shadowOffsetX = 0; context.shadowOffsetY = 0; context.shadowBlur = 0; context.fillStyle = "#ffffaa"; context.fillRect(0, 0, theCanvas.width, theCanvas.height); //Box context.strokeStyle = "#000000"; context.strokeRect(5, 5, theCanvas.width-10, theCanvas.height-10); //Text context.textBaseline = textBaseline; //文字基线 context.textAlign = textAlign; context.font = fontWeight + " " + fontStyle + " " + fontSize + "px " + fontFace; context.shadowColor =shadowColor; context.shadowOffsetX = shadowX; context.shadowOffsetY = shadowY; context.shadowBlur = shadowBlur; context.globalAlpha = textAlpha; var xPosition = (theCanvas.width/2); var yPosition = (theCanvas.height/2); var metrics = context.measureText(message); var textWidth = metrics.width; var tempColor; if (fillType == "colorFill") { tempColor = textFillColor; } else if (fillType == "linearGradient") { var gradient = context.createLinearGradient(xPosition-textWidth/2, yPosition, textWidth, yPosition); gradient.addColorStop(0,textFillColor); gradient.addColorStop(.6,textFillColor2); tempColor = gradient; } else if (fillType == "radialGradient") { var gradient = context.createRadialGradient(xPosition, yPosition, fontSize, xPosition+textWidth, yPosition, 1); gradient.addColorStop(0,textFillColor); gradient.addColorStop(.6,textFillColor2); tempColor = gradient; } else if (fillType == "pattern") { var tempColor = context.createPattern(pattern,"repeat") } else { tempColor = textFillColor; } switch(fillOrStroke) { case "fill": context.fillStyle = tempColor; context.fillText ( message, xPosition ,yPosition); break; case "stroke": context.strokeStyle = tempColor; context.strokeText ( message, xPosition,yPosition); break; case "both": context.fillStyle = tempColor; context.fillText ( message, xPosition ,yPosition); context.strokeStyle = "#000000"; context.strokeText ( message, xPosition,yPosition); break; } } function textBoxChanged(e) { //文本框字符改变时触发 var target = e.target; message = target.value; drawScreen(); } function textBaselineChanged(e) { //文字基线改变时触发 var target = e.target; textBaseline = target.value; drawScreen(); } function textAlignChanged(e) { //文字水平对齐方式改变时触发 var target = e.target; textAlign = target.value; drawScreen(); } function fillOrStrokeChanged(e) { var target = e.target; fillOrStroke = target.value; drawScreen(); } function textSizeChanged(e) { var target = e.target; fontSize = target.value; drawScreen(); } function textFillColorChanged(e) { var target = e.target; textFillColor = "#" + target.value; drawScreen(); } function textFontChanged(e) { var target = e.target; fontFace = target.value; drawScreen(); } function fontWeightChanged(e) { var target = e.target; fontWeight = target.value; drawScreen(); } function fontStyleChanged(e) { var target = e.target; fontStyle = target.value; drawScreen(); } function shadowXChanged(e) { var target = e.target; shadowX = target.value; drawScreen(); } function shadowYChanged(e) { var target = e.target; shadowY = target.value; drawScreen(); } function shadowBlurChanged(e) { var target = e.target; shadowBlur = target.value; drawScreen(); } function shadowColorChanged(e) { var target = e.target; shadowColor = target.value; drawScreen(); } function textAlphaChanged(e) { var target = e.target; textAlpha = (target.value); drawScreen(); } function textFillColor2Changed(e) { var target = e.target; textFillColor2 = "#" + target.value; drawScreen(); } function fillTypeChanged(e) { var target = e.target; fillType = target.value; drawScreen(); } function canvasWidthChanged(e) { var target = e.target; theCanvas.width = target.value; drawScreen(); } function canvasHeightChanged(e) { var target = e.target; theCanvas.height = target.value; drawScreen(); } function canvasStyleSizeChanged(e) { var styleWidth = document.getElementById("canvasStyleWidth"); var styleHeight = document.getElementById("canvasStyleHeight"); var styleValue = "width:" + styleWidth.value + "px; height:" + styleHeight.value +"px;"; theCanvas.setAttribute("style", styleValue ); drawScreen(); } function createImageDataPressed(e) { var imageDataDisplay = document.getElementById("imageDataDisplay"); imageDataDisplay.value = theCanvas.toDataURL(); window.open(imageDataDisplay.value,"canavsImage","left=0,top=0,width=" + theCanvas.width + ",height=" + theCanvas.height +",toolbar=0,resizable=0"); }}</script> </head><body><div style="position: absolute; top: 50px; left: 50px;"><canvas id="canvasOne" width="500" height="300"> Your browser does not support HTML 5 Canvas. </canvas><form> Text: <input id="textBox" placeholder="your text" /> <br> Text Font: <select id="textFont"> <option value="serif">serif</option> <option value="sans-serif">sans-serif</option> <option value="cursive">cursive</option> <option value="fantasy">fantasy</option> <option value="monospace">monospace</option> </select> <br> Font Weight: <select id="fontWeight"> <option value="normal">normal</option> <option value="bold">bold</option> <option value="bolder">bolder</option> <option value="lighter">lighter</option> </select> <br> Font Style: <select id="fontStyle"> <option value="normal">normal</option> <option value="italic">italic</option> <option value="oblique">oblique</option> </select> <br> Text Size: <input type="range" id="textSize" min="0" max="200" step="1" value="50"/> <br> Fill Type : <select id="fillType"> <option value="colorFill">Color Fill</option> <option value="linearGradient">Linear Gradient</option> <option value="radialGradient">Radial Gradient</option> <option value="pattern">pattern</option> </select> <br> Text Color: <input class="color" id="textFillColor" value="FF0000"/> <br> Text Color 2: <input class="color" id="textFillColor2" value ="000000"/> <br> Fill Or Stroke : <select id="fillOrStroke"> <option value="fill">fill</option> <option value="stroke">stroke</option> <option value="both">both</option> </select> <br> Text Baseline <select id="textBaseline"> <option value="middle">middle</option> <option value="top">top</option> <option value="hanging">hanging</option> <option value="alphabetic">alphabetic</option> <option value="ideographic">ideographic</option> <option value="bottom">bottom</option> </select> <br> Text Align <select id="textAlign"> <option value="center">center</option> <option value="start">start</option> <option value="end">end</option> <option value="left">left</option> <option value="right">right</option> </select> <br> Alpha : <input type="range" id="textAlpha" min="0.0" max="1.0" step="0.01" value="1.0"/> <br> Shadow X:<input type="range" id="shadowX" min="-100" max="100" step="1" value="1"/><br>Shadow Y:<input type="range" id="shadowY" min="-100" max="100" step="1" value="1"/> <br>Shadow Blur: <input type="range" id="shadowBlur" min="1" max="100" step="1" value="1" /> <br>Shadow Color: <input class="color" id="shadowColor" value="707070"/> <br> Canvas Width: <input type="range" id="canvasWidth" min="0" max="1000" step="1" value="500"/> <br> Canvas Height: <input type="range" id="canvasHeight" min="0" max="1000" step="1" value="300"/> <br> Canvas Style Width: <input type="range" id="canvasStyleWidth" min="0" max="1000" step="1" value="500"/> <br> Canvas Style Height: <input type="range" id="canvasStyleHeight" min="0" max="1000" step="1" value="300"/> <br> <input type="button" id="createImageData" value="Create Image Data"> <br> <br> <textarea id="imageDataDisplay" rows=10 cols=30></textarea> </form></div></body></html>]]></content>
</entry>
<entry>
<title><![CDATA[canvas学习第二章]]></title>
<url>%2F2017%2F04%2F07%2Fcanvas%2Fcancas%E5%AD%A6%E4%B9%A0%E7%AC%AC%E4%BA%8C%E7%AB%A0%2F</url>
<content type="text"><![CDATA[canvas学习第二章 基础知识 ctx.fillRect(x,y,width,height) 在位置(x,y)处以宽为width,高为height绘制一个矩形 ctx.strokeRect(x,y,width,height) 在位置(x,y)处以宽为width,高为height绘制一个矩形边框,他需要strokeStyle,lineWidth,lineJoin和miterLimit设置 lineCap: 线段末端的形状 butt: 默认值,端点是垂直于线段边缘的平直边缘 round: 端点是在线段边缘处以线宽为直径的半圆 square: 端点是在选段边缘处以线宽为长,以一半线宽为宽的矩形 lineJoin: 定义两条线相交产生的拐角,可将其称为连接 miter: 默认值,在连接外边缘延长相接 bevel: 连接处是一个对角线斜角 round: 连接处是一个圆 ctx.clearRect(x,y,width,height) 在位置(x,y)处以宽为width和高为height清除指定区域并使其完全透明(使用透明黑作为颜色) 当前路径和位图受canvas上下文控制,不属于保存的状态,保存当前状态到堆栈用:ctx.save(),调出最后存储的堆栈恢复画布用ctx.restore() 高级路径画法 ctx.arc(x,y,radius,startAngle,endAngle,anticlockwise) antilockwise若为false,则顺时针画圆,若为true,则逆时针画圆 ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y) 三次贝塞尔曲线 ctx.quadraticCurveTo(cpx,cpy,x,y) 二次贝塞尔曲线 canvas裁切区域 使用ctx.save(),ctx.restore(),ctx.rect(),ctx.clip()可以完成区域裁切 旋转变换 ctx.setTransform(1,0,0,1,0,0)和ctx.rotate() 变换在调用setTransform()或其他变换函数后立即应用到形状与路径上。 缩放变换 ctx.scale() 对图像进行缩放 填充渐变形状 :水平渐变,垂直渐变,对角线渐变,径向渐变 水平渐变:var gr = ctx.createLinearGradient(0,0,100,0); 垂直渐变:var gr = ctx.createLinearGradient(0,0,0,100); 对角线渐变:var gr = ctx.createLinearGradient(0,0,100,100); 径向渐变:var gr = ctx.createRadialGradient(50,50,50,50,100); 给渐变添加断点:gr.addColorStop(.5,’rgb(0,255,0)’); 用图案填充: 四种填充类型:repeat,repeat-x,repeat-y,no-repeat var fill = ctx.createPattern(‘fillImg’,’repeat’); 创建阴影 shadowOffsetX和shadowOffsetY值可以是正也可以为负,负值将会在左侧和上方创建阴影,反之,将会在底部创建阴影。shadowBlur属性用来设置阴影模糊效果的程度。这三个参数不受当前canvas变换矩阵影响,shadowColor可以是任何HTML4颜色字符串。以下代码是关于划线: 12345678910111213141516171819202122232425262728293031function drawScreen() { //round end. bevel join, at top left of canvas context.strokeStyle = "black"; //need list of available colors context.lineWidth=10; context.lineJoin='bevel'; context.lineCap='round'; context.beginPath(); context.moveTo(0, 0); context.lineTo(25, 0); context.lineTo(25,25); context.stroke(); context.closePath(); //round end, bevel join, not at top or left of canvas context.beginPath(); context.moveTo(10, 50); context.lineTo(35, 50); context.lineTo(35,75); context.stroke(); context.closePath(); //flat end, round join, not at top or left context.lineJoin='round'; context.lineCap='butt'; context.beginPath(); context.moveTo(10, 100); context.lineTo(35, 100); context.lineTo(35,125); context.stroke(); context.closePath(); } 以下代码关于区域的裁切: 123456789101112131415161718192021222324252627282930function drawScreen() { //在屏幕上绘制一个黑色的方块 context.fillStyle = "black"; context.fillRect(10, 10, 200, 200); //保存当前上下文 context.save(); context.beginPath(); //裁切画布从(0,0)到(50,50)的正方形 context.rect(0, 0, 50, 50); context.clip(); //绘制红色圆 context.beginPath(); context.strokeStyle = "red"; //need list of available colors context.lineWidth=5; context.arc(100, 100, 100, (Math.PI/180)*0, (Math.PI/180)*360, false); // full circle context.stroke(); context.closePath(); context.restore(); //裁切整个画布 context.beginPath(); context.rect(0, 0, 500, 500); context.clip(); //蓝色圆 context.beginPath(); context.strokeStyle = "blue"; context.lineWidth=5; context.arc(100, 100, 50, (Math.PI/180)*0, (Math.PI/180)*360, false); context.stroke(); context.closePath();} 也可将其它canvas方法配合裁切区域使用,最常见的是arc()函数 以下代码是关于旋转: 12345678910111213function drawScreen(){ //绘制黑色正方形 ctx.fillStyle = 'black'; ctx.fillRect(20,20,25,25); //绘制红色正方形 ctx.setTransform(1,0,0,1,0,0); var angle = 45*Math.PI/180, x = 100,y=100,width=50,height=50; ctx.translate(x+.5*width,y+.5*height); ctx.rotate(angle); ctx.fillStyle = 'red'; ctx.fillRect(-0.5*width,-0.5*height,width,height);} 缩放变换代码如下: 12345678910function drawScreen(){ //绘制一个红色区域 ctx.setTransform(1,0,0,1,0,0); var angle = 45*Math.PI/180, x = 100,y=100,width=50,height=50; ctx.translate(x+.5*width,y+.5*height); ctx.scale(2,2); ctx.fillStyle = 'red'; ctx.fillRect(-0.5*width,-0.5*height,width,height);} 渐变代码如下: 1234567891011121314151617181920212223//径向渐变function drawScreen(){ var gr = ctx.createRadialGradient(100,100,50,100,100,100); //添加颜色断点 gr.addColorStop(0,'rgb(255,0,0)'); gr.addColorStop(0.5,'rgb(0,255,0)'); gr.addColorStop(1,'rgb(0,0,255)'); //使用fillStyle生成渐变 ctx.fillStyle = gr; ctx.arc(100,100,100,0,2*Math.PI,false); ctx.fill();}//图案填充function drawScreen(){ var img = new Image(); img.src = 'fill.gif'; img.onload = function(){ var gr = ctx.createPattern(img,'repeat'); ctx.fillStyle = gr; ctx.fillRect(0,0,200,200); }} 阴影代码如下: 12345678910111213141516171819function drawScreen() { context.fillStyle = 'red'; //设置填充颜色 context.shadowOffsetX=4; context.shadowOffsetY=4; context.shadowColor='black'; context.shadowBlur=4; //模糊度 context.fillRect(10,10,100,100); context.shadowOffsetX=-4; context.shadowOffsetY=-4; context.shadowColor='black'; context.shadowBlur=4; context.fillRect(150,10,100,100); context.shadowOffsetX=10; context.shadowOffsetY=10; context.shadowColor='rgb(100,100,100)'; context.shadowBlur=8; context.arc(200, 300, 100, (Math.PI/180)*0, (Math.PI/180)*360, false); context.fill();}]]></content>
</entry>
<entry>
<title><![CDATA[canvas学习第一章]]></title>
<url>%2F2017%2F04%2F07%2Fcanvas%2Fcanvas%E5%AD%A6%E4%B9%A0%E7%AC%AC%E4%B8%80%E7%AB%A0%2F</url>
<content type="text"><![CDATA[canvas学习第一章 基本知识 ctx.fillStyle = ‘#000000’ 设置文本颜色 ctx.font = ‘20px _sans’ 设置字体大小与字号 ctx.textBaseline = ‘top’ 设置字体垂直对齐方式 ctx.fillText(‘Hello World’,195,80) 将测试文本输出到屏幕上 ctx.strokeStyle = ‘#000000’ 用于方块边框的颜色 ctx.strokeRect(5,5,490,290) 绘制矩形边框 ctx.fillRect(0,0,500,300) 填充矩形 canvas.toDataURL() 这个方法返回的数据是代表当前canvas对象产生的位图字符串,就像屏幕的一个快照,通过提供一个不同MIME类型作为参数,可以返回不同的数据格式。基本的格式是image/png,但也可以获取image/jpeg和其它格式。 绘制图片 12345var image = new Image();image.src = 'hello.gif';image.onload = function(){ //回调函数在image加载成功时触发 ctx.drawImage(image,160,130); //图像对象,与坐标} 我们利用以上知识来制作一个猜字母小游戏123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>CH1Ex4: Guesss The Letter Game </title><script src="modernizr-1.6.min.js"></script><script type="text/javascript">//当页面加载完成时触发eventWindowLoaded方法window.addEventListener('load', eventWindowLoaded, false);var Debugger = function () { };Debugger.log = function (message) { try { console.log(message); } catch (exception) { return; }}function eventWindowLoaded() { canvasApp();}function canvasSupport () { return Modernizr.canvas;}function eventWindowLoaded() { canvasApp();}function canvasApp() { var guesses = 0; //猜的次数 var message = "Guess The Letter From a (lower) to z (higher)"; var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]; var today = new Date(); var letterToGuess = ""; //要猜的字母 var higherOrLower = ""; //键盘按下的字母是大还是小 var lettersGuessed; //键盘按下的字母 var gameOver = false; if (!canvasSupport()) { return; } var theCanvas = document.getElementById("canvasOne"); var context = theCanvas.getContext("2d"); //获得canvas的上下文 initGame(); //初始化游戏 function initGame() { var letterIndex = Math.floor(Math.random() * letters.length); letterToGuess = letters[letterIndex]; //随机生成一个字母 guesses = 0; lettersGuessed = []; //存储已经猜的字母 gameOver = false; //游戏成功与否标注位 window.addEventListener("keyup",eventKeyPressed,true); //监听键盘事件 var formElement = document.getElementById("createImageData"); //生成快照 formElement.addEventListener('click', createImageDataPressed, false); drawScreen(); } function eventKeyPressed(e) { if (!gameOver) { var letterPressed = String.fromCharCode(e.keyCode); //获得键入的字母 letterPressed = letterPressed.toLowerCase(); //转换成小写 guesses++; //猜测次数加一 lettersGuessed.push(letterPressed); //存储到数组中 if (letterPressed == letterToGuess) { //如果猜对了 gameOver = true; //标志位置true } else { letterIndex = letters.indexOf(letterToGuess); //被猜的字母在数组中的位置 guessIndex = letters.indexOf(letterPressed); //键入的字母在数组中的位置 Debugger.log(guessIndex);//控制台调试 if (guessIndex < 0) { //如果输入的不是字母,返回-1 higherOrLower = "That is not a letter"; } else if (guessIndex > letterIndex) { //输入偏大 higherOrLower = "Lower"; } else { higherOrLower = "Higher"; //输入偏小 } } drawScreen(); } } function drawScreen() { //绘制背景 context.fillStyle = "#ffffaa"; context.fillRect(0, 0, 500, 300); //绘制边框 context.strokeStyle = "#000000"; context.strokeRect(5, 5, 490, 290); context.textBaseline = "top"; //绘制日期 context.fillStyle = "#000000"; context.font = "10px _san"; context.fillText (today, 150 ,10); //绘制消息 context.fillStyle = "#FF0000"; context.font = "14px _sans"; context.fillText (message, 125, 30); //绘制猜测次数 context.fillStyle = "#109910"; context.font = "16px _sans"; context.fillText ('Guesses: ' + guesses, 215, 50); //输入是大还是小 context.fillStyle = "#000000"; context.font = "16px _sans"; context.fillText ("Higher Or Lower: " + higherOrLower, 150,125); //Letters Guessed context.fillStyle = "#FF0000"; context.font = "16px _sans"; context.fillText ("Letters Guessed: " + lettersGuessed.toString(), 10, 260); if (gameOver) { context.fillStyle = "#FF0000"; context.font = "40px _sans"; context.fillText ( "You Got it!", 150, 180); } } function createImageDataPressed(e) { window.open(theCanvas.toDataURL(),"canavsImage","left=0,top=0,width=" + theCanvas.width + ",height=" + theCanvas.height +",toolbar=0,resizable=0"); } }</script> </head><body><div style="position: absolute; top: 50px; left: 50px;"><canvas id="canvasOne" width="500" height="300"> Your browser does not support HTML 5 Canvas. </canvas><form><input type="button" id="createImageData" value="Export Canvas Image"></form></div></body></html>]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript的reduce如何工作]]></title>
<url>%2F2017%2F04%2F07%2FJavaScript%2FJavaScript%E7%9A%84Reduce%E6%96%B9%E6%B3%95%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C%2F</url>
<content type="text"><![CDATA[JavaScript的reduce如何工作 一个简单的问题,你如何来求一个数组的和。123const arr = [29,27,2,23];const sum = arr.reduce((total,amount)=>total+amount);console.log(sum); //81 在这个例子中,reduce接受两个参数,total参数和amount参数。 reduce函数循环遍历数组,像for循环一样。 当循环开始时,total参数是最左边的29,amount参数值是27. 函数会把total的值加上amount的值,然后赋值给total。 然后下次函数total的值为56,amount的值为2,一直向右循环,直到没有数据时函数会把total的值返回。 计算数组的平均值。reduce方法中的函数其余的参数,剩余第一个参数的含义为,数据在数组中的下标(从零开始),最后一个为数组本身。123456789101112const arr = [29,27,2,23];const average = arr.reduce((total,amount,index,array)=>{ total+=amount; if(index===array.length-1){ return total/array.length; }else{ return total; }});console.log(average);//20.25``` 如何用reduce方法返回一个数组。上面的例子都是返回一个数值,你也可以返回一个数组,这就需要用到reduce函数的第二个参数。指定reduce方法第一个参数函数的第一个参数。在以前的例子中,我们忽略了它。默认为数组的第一个数据。123456789const arr = [29,27,2,23];const average = arr.reduce((total,amount)=>{ //total为29,amount为27 total+=amount; if(index===array.length-1){ return total/array.length; }else{ return total; }}); 但如果我们给reduce函数加上了第二个参数如:10,代码如下:这时total为10,amount变成了数组的第一个数据29123456789const arr = [29,27,2,23];const average = arr.reduce((total,amount,index,array)=>{ //total为29,amount为27 total+=amount; if(index===array.length-1){ return total/array.length; }else{ return total; }},10); //此时平均值的结果的计算要数组元素加上10,在求平均值22.75 有了上面的知识,我们可以把reduce的第二个参数设置为数组,作为参数传入。就可以返回数组。代码如下(将数组数据乘二返回):1234567const arr = [2,3,4,5];const doubled = arr.reduce((total,amount)=>{ //注意此时的total为[],amount为2 total.push(amount*2); return total;},[]);console.log(doubled); //[4,6,8,10] 我们也可以在其中添加if判断,过滤掉我们不需要的数据123456789const arr = [2,3,4,5];const doubled = arr.reduce((total,amount)=>{ //注意此时的total为[],amount为2 if(amount>3){ total.push(amount*2); } return total;},[]);console.log(doubled); //[8,10] 上面的方法是map和filter方法被reduce方法重写,或许没有map与filter方法更容易让人理解,但reduce方法的好处是你可以综合使用它们,并且操作大量的数据。 我们再来讨论一个问题,你有一个集合,想知道每一个元素在集合中的数目。我们可以利用reduce来解决123456const basket = ['banana','cherry','apple','banana','cherry','apple','orange'];const count = basket.reduce((total,fruit)=>{ total[fruit] = (total[fruit]||0)+1; return total; },{});console.log(count); //{banana: 2, cherry: 2, apple: 2, orange: 1} 使用reduce方法也可以将嵌套数组扁(数组里面嵌套数组)平化为一个数组(与嵌套数组意思相反)12345const data = [[2,3,4,5],[6,7,8,9],[11,22,33,44]];const flat = data.reduce((total,amount)=>{ return total.concat(amount);},[]);console.log(flat); 有时候数据嵌套太深,我们该如何处理,获得data数组中每个对象的中的颜色12345678910111213141516171819202122const data = [{ a: 'happy', b: 'robin', c: ['blue','green'] }, { a: 'tired', b: 'panther', c: ['blue','green','black','blue'] }, { a: 'sad', b: 'gold', c: ['blue','red'] }, ];const colors = data.reduce((total,amount)=>{ amount.c.forEach(color=>{ total.push(color); }); return total;},[]); 那我们如何获取颜色种类呢(去掉重复的颜色,只保留一个)123456789101112131415161718192021222324const data = [{ a: 'happy', b: 'robin', c: ['blue','green'] }, { a: 'tired', b: 'panther', c: ['blue','green','black','blue'] }, { a: 'sad', b: 'gold', c: ['blue','red'] }, ];const colors = data.reduce((total,amount)=>{ amount.c.forEach(color=>{ if(total.indexOf(amount)===-1){ total.push(color); } }); return total;},[]); reduce的另一个强大之处是他可以处理函数假设我们有一个函数集合,这些函数可以允许我们增加,减少,相乘,折半某一个数字123456789101112function increment(input){ return input+1;}function decrement(input){ return input-1;}function double(input){ return input*2;}function half(input){ return input/2;} 在某种情况下,我们需要对某一个数进行增加,减少,相乘,折半。我们需要写一个函数,返回((input+1)*2-1)/2,我们可以利用reduce函数创造一个管道来解决。 管道是一系列函数把一个初始值转化为最终值的过程 代码如下: 12345var pipeline = [increment,double,decrement,half];const result = pipeline.reduce((total,fun)=>{ return fun(total);},2);console.log(result);]]></content>
</entry>
<entry>
<title><![CDATA[时间片轮转与高响应比优先算法]]></title>
<url>%2F2017%2F04%2F02%2FOS%2F%E6%97%B6%E9%97%B4%E7%89%87%E8%BD%AE%E8%BD%AC%E4%B8%8E%E9%AB%98%E5%93%8D%E5%BA%94%E6%AF%94%E4%BC%98%E5%85%88%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95%2F</url>
<content type="text"><![CDATA[时间片轮转与高响应比优先算法 轮转调度算法轮转法的基本原理 在轮转(RR)法中,系统根据FCFS策略,将所有的就绪队列排成一个就绪队列,并可设置一定时间间隔(如30ms)产生一次终断,激活系统中的进程调度程序,完成一次调度,将CPU分配给队首进程,另其执行。当该进程的时间片耗尽或运行完毕时,系统再次将CPU分配给新的队首进程(或新到达的紧迫进程),由此,可保证就绪队列中的所有进程在一个确定的时间片内,都能获得一次CPU执行。 进程切换时机 在RR调度算法中,应在何事进行进程的切换,可分为两种情况: 若一个时间片尚未用完,正在运行的进程便已经完成,就立即激活调度程序,将他从就绪队列中删除,再调度就绪队列中队首的进程运行,并启动一个新的时间片。 在一个时间片用完时,计时器中断处理程序被激活。如果进程尚未运行完毕,调度程序就把他送往就绪队列的尾部。 时间片大小确定 如果选择的时间片小,将有利于短作业,因为它能在该时间片内完成。但时间片小,意味着会进行频繁的进程调度和进程上下文的切换,无疑会增加系统的开销。反之,若时间片选择得太长,且为使每个进程都能在一个时间片内完成。RR算法便会退化成FCFS算法,无法满足短作业和交互式用户的需求。 一个较为可取的时间片大小是略大于一次典型的交互所需要的时间,使大多数交互式进程能在一个时间片内完成 高响应比优先调度算法高响应比优先调度算法为每一个作业引入一个动态优先级,即优先级是可以改变的,令他随等待时间延长而增加,这将使长作业的优先级在等待期间不断的增加,等到足够的时间后,必然会有机会获得处理机。该优先级变化规律为: 优先级 = (等待时间+要求服务时间)/要求服务时间 由于等待时间与服务时间之和就是系统对该作业的响应时间,故该优先级又相当于响应比Rp。优先级又可表示为: 优先级 = (等待时间+要求服务时间)/要求服务时间 = 响应时间/要求服务时间 由上式可以看出 如果作业的等待时间相同,则要求服务的时间愈短,其优先级愈高,因而类似于SJF算法,有利于短作业。 当要求服务的的时间相同时,作业的优先级又取决于其等待时间,因而又类似于FCFS算法。 对于长作业的优先级,可以随等待时间的增加而增大,当其等待时间足够长时,也可获得处理机。 在每次进行调度前,都需要进行响应比的计算,显然会增加系统开销。 两种算法代码实现如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128//进程类function Process(){}Process.list = []; //进程列表Process.task_num = 5;//进程数Process.regetRatioAndGetProcess = function(list){ //获得响应比 list.forEach(function(item){ item.ratio = ((Date.parse(new Date()) - item.arriveTime) / item.runTime) +1; //一个进程结束后,重新计算响应比 }); var maxRatio = 0,NO = -1; list.forEach(function(item,index){ //挑选出响应比最大的进程 if(item.ratio>maxRatio){ maxRatio = item.ratio; NO = index; } }); return list.splice(NO,1)[0];}//进程初始化Process.init = function(){ Process.list.splice(0,Process.list.length); for(var i = 0;i<Process.task_num;i++){ Process.list.push({ id: i,//进程号 arriveTime: 0, //进程到达时间 ratio: 0, //响应比 runTime: (Math.floor(Math.random()*4)+2)*1000, //运行时间间隔为[2,6]s }); }}//-----------------------------------------------------------//高响应比优先调度算法var HRRN = { list:[], //记录进程响应时间 init_task: function(list,num){ list.forEach(function(item){ item.arriveTime = Date.parse(new Date()); //获得进程到达时间 }); }, //进程运行 run: function(list,num){ for(var i = 0;i<num;i++){ var runItem = Process.regetRatioAndGetProcess(list); //得到响应比最大的进程 console.log(`第${runItem.id}号进程开始运行:${new Date()}`); var t = Date.parse(new Date()); var exit = t + runItem.runTime; while(true){ //模拟进程运行 if(Date.parse(new Date()) >=exit){ break; } } console.log(`第${runItem.id}号进程结束运行:${new Date()}`); //记录进程的响应时间 :现在时间-到达时间 HRRN.list.push({ id: runItem.id, responseTime: Date.parse(new Date()) - runItem.arriveTime }); } }, //打印进程响应时间,计算平均响应周期 show:function(){ var total = 0; HRRN.list.forEach(function(item){ console.log(`${item.id}的响应时间为${Math.floor(item.responseTime)}`); total+=Math.floor(item.responseTime); }); console.log(`平均周转周期为${Math.floor(total/HRRN.list.length)/1000}s`); }}Process.init();HRRN.init_task(Process.list,Process.task_num);HRRN.run(Process.list,Process.task_num);HRRN.show();//---------------------------------------------------------------//时间片轮转算法var RR = { circle_size:4000,//时间片大小 list:[], //记录进程执行时间 init_task:function(list){ //初始化进程 list.forEach(function(item){ item.arriveTime = Date.parse(new Date()); }); }, run:function(list){ while(true){ if(list.length===0) break; //进程全部运行完成后,退出死循环 var item = list.splice(0,1)[0]; //选出队首进程 var runTime = item.runTime; var id = item.id; console.log(`第${id}号进程开始运行 :${new Date()}`); if(runTime<RR.circle_size){ //如果能够在本时间片内运行完 var exit = Date.parse(new Date()) + runTime; while((Date.parse(new Date()))< exit); console.log(`第${id}号进程结束运行,运行时间为${runTime/1000}s :${new Date()}`); RR.list.push({ id: item.id, responseTime: Date.parse(new Date()) - item.arriveTime }); }else{ //计算下一次需要的运行时间 item.runTime -= RR.circle_size; list.push(item); var exit = Date.parse(new Date()) + RR.circle_size; while((Date.parse(new Date()))< exit); console.log(`${new Date()}:第${id}号进程时间片用完,处于等待状态`); } } }, //打印每个进程的响应时间与平均周转周期 show:function(){ var total = 0; RR.list.forEach(function(item){ console.log(`${item.id}的响应时间为${Math.floor(item.responseTime)}`); total+=Math.floor(item.responseTime); }); console.log(`平均周转周期为${Math.floor(total/RR.list.length)/1000}s`); }}Process.init();RR.init_task(Process.list);RR.run(Process.list);RR.show();]]></content>
</entry>
<entry>
<title><![CDATA[JQuery的Deferred对象详解]]></title>
<url>%2F2017%2F04%2F02%2FJavaScript%2Fjquery%E7%9A%84Deferred%E5%AF%B9%E8%B1%A1%E8%AF%A6%E8%A7%A3%2F</url>
<content type="text"><![CDATA[JQuery的Deferred对象就是Jquery的回调函数的解决方案 在我们开发网站的过程中,我们经常会遇到很长的JavaScript操作。其中,既有异步的操作,如ajax读取服务器的数据,也有异步的操作,如遍历一个大型数组,他们都不是能立即返回结果的。通常的做法是,为他们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。简单地说,Deferred对象就是JQuery的回调函数的解决方案。在英语中,defer的意思是“延迟”,所以Deferred对象的含义是延迟到未来的某个点在执行。 ajax的链式写法123456789$.ajax({ url: 'test.html', success: function(){ alert('ok'); }, error: function(){ alert('fail'); } }); 在上面的代码中,$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调方法,error方法指定操作失败后的回调函数。 $.ajax()操作完成后,如果使用的是低于1.5.0版本的JQuery,返回的是XHR对象,没法进行链式操作;如果高于1.5.0版本,返回的是Deferred对象,可以进行链式操作。 1234567$.ajax('text.html').done(function(){ alert('ok'); }).fail(function(){ alert('fail');}); 指定同一操作的多个回调函数Deferred对象的一大好处是允许你自由添加多个回调函数。还是以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,还想再运行一个回调函数。代码如下:12345678910$.ajax('test.html').done(function(){ alert('ok');}).fail(function(){ alert('fail')}).done(function(){ alert('第二个回调');}); 回调函数可以添加任意多个,它们按添加顺序执行 为多个操作指定回调函数Deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。此时用到一个新的方法:$.when(): 1234567$.when($.ajax('test1.html'),$.ajax('test2.html')).done(function(){ alert('ok') }).fail(function(){ alert('fail');}); 这段代码的意思是,先执行两个操作$.ajax()操作,如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。 普通函数的回调函数接口(上)Deferred对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。也就是说,任何一个操作(不管是ajax操作还是本地操作,也不管是异步操作还是同步操作,都可以使用Deferred对象的各种方法,指定回调函数)。 来看一个具体的例子。假如有一个很耗时的操作wait:123456var wait = function(){ var tasks = function(){ alert('执行完毕'); }; setTimeout(tasks,5000);}; 为它指定回调函数,应该怎么做呢?1234567$.when(wait()).done(function(){ alert('haha');}).fail(function(){ alert('fail');}); 但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于$.when()的参数只能是Deferred对象,所以必须对wait()改写: 123456789var dtd = $.Deferred(); //新建一个Deferred对象var wait = function(dtd){ var tasks = function(){ alert('执行完毕'); dtd.resolve(); }; setTimeout(tasks,5000); return dtd;}; 现在,wait()函数返回的是Deferred对象,这样就可以加上链式操作了。1234567$.when(wait(dtd)).done(function(){ alert('haha');}).fail(function(){ alert('fail');}); wait()函数运行完,就会自动运行done()方法指定的回调函数。 deferred.resolve()方法和deferred.reject()方法JQuery规定,Deferred对象有三种执行状态:未完成,已完成和已失败。如果执行状态是“已完成”(resolved),Deferred对象立即调用done()方法指定的回调函数;如果执行状态是“已失败”(rejected),调用fail()方法指定的回调函数;如果执行状态是“未完成”(pending),调用progress()方法指定的回调函数。 前面部分的ajax操作,Deferred对象会根据返回结果,自动改变自身的执行状态;但在wait()函数中,这个状态必须有程序员手动指定。dtd.resolve()的意思是,将Deferred对象的执行状态从“未完成”改为“已完成”,从而触发done()方法。类似的,还存在一个deferred.reject()方法,作用是将Deferred对象的执行状态从“未完成”改为“已失败”,从而触发fail()方法。 Deferred对象的promise()方法上面这种写法,还是有问题。那就是dtd是一个全局对象,所以它的执行状态可以从外部改变。看如下代码:1234567891011121314151617var dtd = $.Deferred(); //新建一个Deferred对象var wait = function(dtd){ var tasks = function(){ alert('执行完毕'); dtd.resolve(); }; setTimeout(tasks,5000); return dtd;};$.when(wait(dtd)).done(function(){ alert('haha');}).fail(function(){ alert('fail');});dtd.resolve(); 在其尾部添加一行dtd.resolve(),这就改变了dtd对象的执行状态,因此导致done()方法立即执行,先跳出“haha”,然后5秒后在跳出”执行完毕”的提示框。为了避免这种情况,JQuery提供了12345678910111213141516```JavaScript var dtd = $.Deferred(); // 新建一个Deferred对象 var wait = function(dtd){ var tasks = function(){ alert("执行完毕!"); dtd.resolve(); // 改变Deferred对象的执行状态 }; setTimeout(tasks,5000); return dtd.promise(); // 返回promise对象 }; var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作 $.when(d) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); }); d.resolve(); // 此时,这个语句是无效的 在上面的这段代码中,wait()函数返回的是promise对象。然后,我们把回调函数绑定到这个对象上,而不是原来的Deferred对象。这样的好处是,无法改变这个对象的执行状态,要想改变执行状态,只能操作原来的Deferred对象。 不过最好的写法如下:12345678910111213 var wait = function(dtd){var dtd = $.Deferred(); // 新建一个Deferred对象 var tasks = function(){ alert("执行完毕!"); dtd.resolve(); // 改变Deferred对象的执行状态 }; setTimeout(tasks,5000); return dtd.promise(); // 返回promise对象 }; var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作 $.when(d) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); }); Deferred对象的方法 $.Deferred() 生成一个Deferred对象 deferred.progress() 该方法用于指定deferred对象状态为等待中的回调函数。但是她仅在deferred对象生成了进度通知时才会被调用。 12345678910111213141516171819202122232425262728293031323334353637383940414243 var wait = function() { var dtd = $.Deferred(); // 新建一个deferred对象 var i = 1, timer, percent; // 记录进度 var tasks = function() { if (i == 11) { alert("执行完毕!"); dtd.resolve(); // 此操作完成后改变deferred对象的执行状态 } else { percent = (i * 500) / 5000 * 100 + '%'; dtd.notify(percent); // 调用progress回调 i++; setTimeout(tasks, 500); } }; setTimeout(tasks, 1000); return dtd;};// 绑定回调函数$.when(wait()) .done(function() { alert("执行成功了!"); }) .fail(function() { alert("出错啦!"); }) .progress(function(data) { console.log('执行中,已完成', data); });// 执行中,已完成 10%// 执行中,已完成 20%// 执行中,已完成 30%// 执行中,已完成 40%// 执行中,已完成 50%// 执行中,已完成 60%// 执行中,已完成 70%// 执行中,已完成 80%// 执行中,已完成 90%// 执行中,已完成 100%// 之后弹出 执行完毕!和 执行成功了! jQuery3.0以上版本对when方法做了大幅调整。向promise/A+靠齐,上面的写法中notify是触发不了when中的progress回调的,需要使用promise来给对象部署deferred接口或使用$.Deferred()传入函数名。 promise 给一个对象部署Deferred接口 1234567891011121314151617181920212223242526272829303132var dtd = $.Deferred(); // 新建一个deferred对象var wait = function(dtd) { var i = 1, timer, percent; // 记录进度 var tasks = function() { if (i == 11) { alert("执行完毕!"); dtd.resolve(); // 此操作完成后改变deferred对象的执行状态 } else { percent = (i * 500) / 5000 * 100 + '%'; dtd.notify(percent); // 调用progress回调 i++; setTimeout(tasks, 500); } }; setTimeout(tasks, 1000); };// 在wait对象上部署Deferred接口,此后就可以直接在wait上使用deferred对象promise后的方法了dtd.promise(wait);// 在wait对象上使用deferred对象的方法指定回调。wait.done(function() { alert("执行成功了!");}).fail(function() { alert("出错啦!");}).progress(function(data) { console.log('执行中,已完成', data);});// 执行wait(dtd); 使用$.Deferred传入函数名: 123456789101112131415161718192021222324252627282930var wait = function(dtd) { var i = 1, timer, percent; // 记录进度 var tasks = function() { if (i == 11) { alert("执行完毕!"); dtd.resolve(); // 此操作完成后改变deferred对象的执行状态 } else { percent = (i * 500) / 5000 * 100 + '%'; dtd.notify(percent); // 调用progress回调 i++; setTimeout(tasks, 500); } }; setTimeout(tasks, 1000); return dtd;};// 绑定回调函数$.Deferred(wait) //$.Deferred()的返回值将作为wait函数的参数 .done(function() { alert("执行成功了!"); }) .fail(function() { alert("出错啦!"); }) .progress(function(data) { console.log('执行中,已完成', data); }); deferred.done() 指定操作成功时的回调函数 deferred.fail() 指定操作失败时的回调函数 deferred.promise() 没有参数时,返回一个新的Deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口。 deferred.resolve() 手动改变deferred对象的运行状态为“已完成”,从而触发done()方法。 deferred.always() 不管Deferred对象如何,总是执行。 deferred.reject() 这个方法与deferred.resolve()正好相反,调用后将Deferred对象的运行状态变为“已失败”,从而立即触发fail()方法。 $.when() 为多个操作指定回调函数。 deferred.then() 有时为了省事,可以把done()和fail()合写在一起,这就是then()方法。 12$.when($.ajax('test.html')) .then(successFunc,failureFunc); 如果then()有两个参数,那么第一个参数时done()方法的回调函数。如果then()只有一个参数,那么等同于done()。]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript函数柯里化]]></title>
<url>%2F2017%2F03%2F22%2FJavaScript%2F%E5%87%BD%E6%95%B0%E6%9F%AF%E9%87%8C%E5%8C%96%2F</url>
<content type="text"><![CDATA[什么是函数柯里化 柯里化(Curring),又称为部分求值,是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。 柯里化特点 接受单一参数,将更多的参数通过回调函数来解决。 返回一个新的函数,用于处理所有的想要传入的参数。 需要利用call/apply与arguments伪数组收集参数。 返回的函数正是用来处理收集起来的参数。 需要理解JavaScript函数的隐式转换JavaScript是一种弱类型语言,它的隐式转换是非常灵活的。如下: 1234function fn(){ return 5;}console.log(fn+10); 试着运行一下你会发现答案为:123function fn(){ return 5;}10 接着我们在修改代码为:1234567function fn(){ return 5;}fn.toString = function(){ return 10;}console.log(fn+10); 你会发现答案为:20,接着我们在进行修改12345678910function fn(){ return 5;}fn.toString = function(){ return 10;}fn.valueOf = function(){ return 20;}console.log(fn+10); 答案为30 当我们使用console.log()或者进行运算时,隐式转换就可能会发生。从上面的例子我们可以得到如下结论: 当我们没有重新定义toString()与valueOf时,函数的隐式转换会调用默认的toString方法,他会将函数的定义内容转化为字符串返回。当我们主动定义了toString()/valueOf时,那么隐式转化的返回结果则由我们自己控制了。其中valueOf的优先级会比toString()高。 需要知道如何利用call/apply封装数组的map方法 map:对数组的每一项运行给定的函数,将每次函数调用返回的结果组成新的数组。 具体实现如下:12345678910111213141516171819Array.prototype._map = function(fn,ctx){ var list = this, temp = []; //用来存储返回的新值 console.log(list); if(typeof fn =='function'){ //遍历数组的每一项 list.forEach(function(item,index){ temp.push(fn.call(ctx,item,index)); }); }else{ console.err('TypeError:'+fn+' must be a function'); } return temp;}var arr = [2,3,4,5]._map(function(item,index){ return item + index;});console.log(arr); 理解函数柯里化 考虑实现一个add方法,使结果能够满足如下预期:add(1)(2) = 3add(1,2,3)(4) = 10add(1)(2)(3)(4)(5) = 15 一开始如果只有两个参数,你可能会这样写:12345function add(a){ return function(b){ return a+b; }} 如果有三个的话,可以这样写:1234567function add(a){ return function(b){ return function(c){ return a+b+c; } }} 如果有n个呢,难道要一直嵌套下去,而且2,3种情况也没有覆盖到。所以,要利用函数柯里化,利用闭包的特性,将所有参数,集中到最后的返回函数里进行计算并返回结果。因此,我们在写代码时,要将所有的参数集中起来处理。具体实现如下:12345678910111213141516171819function getSum(rest){ var sum = 0; rest.forEach((item)=>{ return sum+=item; }); return sum;}function add (...rest){ var _args = rest; var _adder = function(...rest){ [].push.apply(_args, rest); return _adder; } _adder.toString = function(){ return getSum(_args); } return _adder();} 我们再来考虑函数柯里化的例子 假如有一个计算要求,需要我们将数组里面的每一项用我们自己想要的字符给连接起来。我们该怎么做? 123456var arr = [1,2,3,4,5];Array.prototype.merge = function(chars){ return this.join(chars);}var str = arr.merge('-');console.log(str); 在考虑将数组每一位加一位数在连接起来 12345678var arr = [1,2,3,4];Array.prototype.merge = function(chars,number){ return this.map((item)=>{ return item+number; }).join(chars);}var str = arr.merge('-',7);console.log(str); 我们并不知道自己要对数组进行何种处理,所以我们要把对数组的操作封装起来。我们现在只知道需要将他们处理后用字符串连接起来,所以不妨将他们的处理内容保存在函数中,而仅仅固定封装连起来的部分。 12345678910111213141516171819202122232425262728Array.prototype.merge = function(fn,chars){ return this.map(fn).join(chars);}var arr = [1,2,3,4];var add = function(num){ return function(item){ return num + item; }}var reduce = function(num){ return function(item){ return item - num; }}//每一项加2合并var res1 = arr.merge(add(2),'-');//每一项减2合并var res2 = arr.merge(reduce(2),'-');//也可以直接传入回调函数var res3 = arr.merge((function(num){ return function(item){ return item + num; }})(2),'-'); 柯里化通用式1234567891011121314var currying = function(fn){ var args = [].slice.apply(arguments,[1]); return function(){ var _args = args.concat([].slice.apply(arguments)); return fn.apply(null,_args); }}var sum = currying(function(){ var list = [].slice.call(arguments); return list.reduce(function(a,b){ return a + b; });},10); 柯里化与bind1234567Object.prototype.bind = function(ctx){ var _args = [].slice.call(arguments,1); var fn = this; return function(){ fn.apply(ctx,_args.concat([].slice.call(arguments))); }} 参考链接 函数柯里化]]></content>
</entry>
<entry>
<title><![CDATA[JavaScript内置函数]]></title>
<url>%2F2017%2F01%2F05%2FJavaScript%2FJavaScript%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0%E8%A7%A3%E6%9E%90%2F</url>
<content type="text"><![CDATA[这篇文章将介绍几个关于JavaScript内置函数的简单算法 将字符串重复一定次数使用while循环1234567function repeat(num,str){ var repeatStr = ''; while(num--){ repeatStr+=str; } return repeatStr;} 使用递归12345678910function repeatStringNumTimes(str,times){ if(times<0){ return ''; } if(times===1){ return str; }else{ return str + repeatStringNumTimes(str,times-1); }} 使用es6的repeat123function repeatStringNumTimes(str,times){ return times<0?'':str.repeat(times);} 检查某一字符串是否已特定字符串结束利用substr函数123function confirmEnding(str,target){ return str.substr(-target.length)===target? true:false;} 使用内置函数123function confirmEnding(str,target){ return str.endsWith(target);} 翻转字符串使用split(),reverse(),join()123function reverseStr(str){ return str.split('').reserve().join('');} 使用for循环12345678function reverseStr(str){ var newStr = ''; var len = str.length; for(let i = len-1;i>=0;i--){ newStr+=str[i]; } return newStr;} 使用递归123456function reverseStr(str){ if(str===''){ return ''; } return str.charAt(0)+reverseStr(str.substr(1));} 将一个句子的每个单词首字母大写,其余小写使用for循环12345678function titleCase(str){ var list = str.toLowerCase().split(' '); var len = list.length; for(let i = 0 ;i<len;i++){ list[i] = list[i].charAt(0).toUpperCase() + list[i].substr(1); } return list.join(' ');} 使用map方法12345function titleCase(str){ return str.toLowerCase().split(' ').map(function(word){ return word.charAt(0).toUpperCase()+ word.slice(1); }).join('');} 使用replace方法12345function titleCase(str){ return str.toLowerCase().split(' ').map(function(word){ return word.replace(word[0],word[0].toUpperCase()); }).join('');}]]></content>
</entry>
</search>