JS原生常用API大全

用devicePixelRatio判断当前浏览器是否处于缩放状态




1
2
3
4
5
var a={n:1};
var b=a;
a.x=a.y=a={n:2}; //a.x={n:2},a.y={n:2},此时a还是{n:1},a={n:2}
console.log(a); // {n: 2}
console.log(b); //{n: 1, y: {…}, x: {…}}

ES6模块是编译时加载是什么意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
JS的“编译”可能指三种流程:transform、bundle、uglify。

transform:把ES6+的JS转译成es5或以下引擎能运行的代码。
bundle:合并依赖的模块。
uglify:压缩丑化,减小体积。

这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高
ES6模块是编译时加载:模块之间的依赖关系,在运行之前(即编译时)就通过静态分析来确定好了的。

浏览器JS引擎遇到
<script type="module">
import {addTextToBody} from './utils.js';
addTextToBody('Modules are pretty cool.');
</script>
既完成模块的引用确定,V8在编译js的既完成依赖关系,比CommonJS运行时才去加载效率高

原生JS API

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
一、节点
1.1 节点属性
Node.nodeName //返回节点名称,只读
Node.nodeType //返回节点类型的常数值,只读
Node.nodeValue //返回Text或Comment节点的文本值,只读
Node.textContent //返回当前节点和它的所有后代节点的文本内容,可读写
Node.baseURI //返回当前网页的绝对路径

Node.ownerDocument //返回当前节点所在的顶层文档对象,即document
Node.nextSibling //返回紧跟在当前节点后面的第一个兄弟节点
Node.previousSibling //返回当前节点前面的、距离最近的一个兄弟节点
Node.parentNode //返回当前节点的父节点
Node.parentElement //返回当前节点的父Element节点
Node.childNodes //返回当前节点的所有子节点
Node.firstChild //返回当前节点的第一个子节点
Node.lastChild //返回当前节点的最后一个子节点

//parentNode接口
Node.children //返回指定节点的所有Element子节点
Node.firstElementChild //返回当前节点的第一个Element子节点
Node.lastElementChild //返回当前节点的最后一个Element子节点
Node.childElementCount //返回当前节点所有Element子节点的数目。
1.2 操作
Node.appendChild(node) //向节点添加最后一个子节点
Node.hasChildNodes() //返回布尔值,表示当前节点是否有子节点
Node.cloneNode(true); // 默认为false(克隆节点), true(克隆节点及其属性,以及后代)
Node.insertBefore(newNode,oldNode) // 在指定子节点之前插入新的子节点
Node.removeChild(node) //删除节点,在要删除节点的父节点上操作
Node.replaceChild(newChild,oldChild) //替换节点
Node.contains(node) //返回一个布尔值,表示参数节点是否为当前节点的后代节点。
Node.compareDocumentPosition(node) //返回一个7个比特位的二进制值,表示参数节点和当前节点的关系
Node.isEqualNode(noe) //返回布尔值,用于检查两个节点是否相等。所谓相等的节点,指的是两个节点的类型相同、属性相同、子节点相同。
Node.normalize() //用于清理当前节点内部的所有Text节点。它会去除空的文本节点,并且将毗邻的文本节点合并成一个。

//ChildNode接口
Node.remove() //用于删除当前节点
Node.before() //
Node.after()
Node.replaceWith()
1.3 Document节点
1.3.1 Document节点的属性
document.doctype //
document.documentElement //返回当前文档的根节点
document.defaultView //返回document对象所在的window对象
document.body //返回当前文档的<body>节点
document.head //返回当前文档的<head>节点
document.activeElement //返回当前文档中获得焦点的那个元素。

//节点集合属性
document.links //返回当前文档的所有a元素
document.forms //返回页面中所有表单元素
document.images //返回页面中所有图片元素
document.embeds //返回网页中所有嵌入对象
document.scripts //返回当前文档的所有脚本
document.styleSheets //返回当前网页的所有样式表

//文档信息属性
document.documentURI //表示当前文档的网址
document.URL //返回当前文档的网址
document.domain //返回当前文档的域名
document.lastModified //返回当前文档最后修改的时间戳
document.location //返回location对象,提供当前文档的URL信息
document.referrer //返回当前文档的访问来源
document.title //返回当前文档的标题
document.characterSet属性返回渲染当前文档的字符集,比如UTF-8、ISO-8859-1。
document.readyState //返回当前文档的状态
document.designMode //控制当前文档是否可编辑,可读写
document.compatMode //返回浏览器处理文档的模式
document.cookie //用来操作Cookie
1.3.2 Document节点的方法
(1)读写方法
document.open() //用于新建并打开一个文档
document.close() //不安比open方法所新建的文档
document.write() //用于向当前文档写入内容
document.writeIn() //用于向当前文档写入内容,尾部添加换行符。
(2)查找节点
document.querySelector(selectors) //接受一个CSS选择器作为参数,返回第一个匹配该选择器的元素节点。
document.querySelectorAll(selectors) //接受一个CSS选择器作为参数,返回所有匹配该选择器的元素节点。
document.getElementsByTagName(tagName) //返回所有指定HTML标签的元素
document.getElementsByClassName(className) //返回包括了所有class名字符合指定条件的元素
document.getElementsByName(name) //用于选择拥有name属性的HTML元素(比如<form>、<radio>、<img>、<frame>、<embed>和<object>等)
document.getElementById(id) //返回匹配指定id属性的元素节点。
document.elementFromPoint(x,y) //返回位于页面指定位置最上层的Element子节点。
(3)生成节点
document.createElement(tagName) //用来生成HTML元素节点。
document.createTextNode(text) //用来生成文本节点
document.createAttribute(name) //生成一个新的属性对象节点,并返回它。
document.createDocumentFragment() //生成一个DocumentFragment对象
(4)事件方法
document.createEvent(type) //生成一个事件对象,该对象能被element.dispatchEvent()方法使用
document.addEventListener(type,listener,capture) //注册事件
document.removeEventListener(type,listener,capture) //注销事件
document.dispatchEvent(event) //触发事件
(5)其他
document.hasFocus() //返回一个布尔值,表示当前文档之中是否有元素被激活或获得焦点。
document.adoptNode(externalNode) //将某个节点,从其原来所在的文档移除,插入当前文档,并返回插入后的新节点。
document.importNode(externalNode, deep) //从外部文档拷贝指定节点,插入当前文档。
1.4 Element节点
1.4.1 Element节点的属性
(1)特性属性
Element.attributes //返回当前元素节点的所有属性节点
Element.id //返回指定元素的id属性,可读写
Element.tagName //返回指定元素的大写标签名
Element.innerHTML //返回该元素包含的HTML代码,可读写
Element.outerHTML //返回指定元素节点的所有HTML代码,包括它自身和包含的的所有子元素,可读写
Element.className //返回当前元素的class属性,可读写
Element.classList //返回当前元素节点的所有class集合
Element.dataset //返回元素节点中所有的data-*属性。
(2)尺寸属性
Element.clientHeight //返回元素节点可见部分的高度
Element.clientWidth //返回元素节点可见部分的宽度
Element.clientLeft //返回元素节点左边框的宽度
Element.clientTop //返回元素节点顶部边框的宽度
Element.scrollHeight //返回元素节点的总高度
Element.scrollWidth //返回元素节点的总宽度
Element.scrollLeft //返回元素节点的水平滚动条向右滚动的像素数值,通过设置这个属性可以改变元素的滚动位置
Element.scrollTop //返回元素节点的垂直滚动向下滚动的像素数值
Element.offsetHeight //返回元素的垂直高度(包含border,padding)
Element.offsetWidth //返回元素的水平宽度(包含border,padding)
Element.offsetLeft //返回当前元素左上角相对于Element.offsetParent节点的垂直偏移
Element.offsetTop //返回水平位移
Element.style //返回元素节点的行内样式
(3)节点相关属性
Element.children //包括当前元素节点的所有子元素
Element.childElementCount //返回当前元素节点包含的子HTML元素节点的个数
Element.firstElementChild //返回当前节点的第一个Element子节点
Element.lastElementChild //返回当前节点的最后一个Element子节点
Element.nextElementSibling //返回当前元素节点的下一个兄弟HTML元素节点
Element.previousElementSibling //返回当前元素节点的前一个兄弟HTML节点
Element.offsetParent //返回当前元素节点的最靠近的、并且CSS的position属性不等于static的父元素。
1.4.2 Element节点的方法
(1)位置方法
getBoundingClientRect()
// getBoundingClientRect返回一个对象,包含top,left,right,bottom,width,height // width、height 元素自身宽高
// top 元素上外边界距窗口最上面的距离
// right 元素右外边界距窗口最上面的距离
// bottom 元素下外边界距窗口最上面的距离
// left 元素左外边界距窗口最上面的距离
// width 元素自身宽(包含border,padding)
// height 元素自身高(包含border,padding)

getClientRects() //返回当前元素在页面上形参的所有矩形。

// 元素在页面上的偏移量
var rect = el.getBoundingClientRect()
return {
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
}
(2)属性方法
Element.getAttribute():读取指定属性
Element.setAttribute():设置指定属性
Element.hasAttribute():返回一个布尔值,表示当前元素节点是否有指定的属性
Element.removeAttribute():移除指定属性
(3)查找方法
Element.querySelector()
Element.querySelectorAll()
Element.getElementsByTagName()
Element.getElementsByClassName()
(4)事件方法
Element.addEventListener():添加事件的回调函数
Element.removeEventListener():移除事件监听函数
Element.dispatchEvent():触发事件

//ie8
Element.attachEvent(oneventName,listener)
Element.detachEvent(oneventName,listener)

// event对象
var event = window.event||event;

// 事件的目标节点
var target = event.target || event.srcElement;

// 事件代理
ul.addEventListener('click', function(event) {
if (event.target.tagName.toLowerCase() === 'li') {
console.log(event.target.innerHTML)
}
});
(5)其他
Element.scrollIntoView() //滚动当前元素,进入浏览器的可见区域

//解析HTML字符串,然后将生成的节点插入DOM树的指定位置。
Element.insertAdjacentHTML(where, htmlString);
Element.insertAdjacentHTML('beforeBegin', htmlString); // 在该元素前插入
Element.insertAdjacentHTML('afterBegin', htmlString); // 在该元素第一个子元素前插入
Element.insertAdjacentHTML('beforeEnd', htmlString); // 在该元素最后一个子元素后面插入
Element.insertAdjacentHTML('afterEnd', htmlString); // 在该元素后插入

Element.remove() //用于将当前元素节点从DOM中移除
Element.focus() //用于将当前页面的焦点,转移到指定元素上
二、CSS操作
(1)类名操作
//ie8以下
Element.className //获取元素节点的类名
Element.className += ' ' + newClassName //新增一个类名

//判断是否有某个类名
function hasClass(element,className){
return new RegExp(className,'gi').test(element.className);
}

//移除class
function removeClass(element,className){
element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'),'');
}

//ie10
element.classList.add(className) //新增
element.classList.remove(className) //删除
element.classList.contains(className) //是否包含
element.classList.toggle(className) //toggle class
(2)style操作
element.setAttribute('style','')

element.style.backgroundColor = 'red'

element.style.cssText //用来读写或删除整个style属性

element.style.setProperty(propertyName,value) //设置css属性
element.style.getPropertyValue(property) //获取css属性
element.style.removeProperty(property) //删除css属性
操作非内联样式
//ie8
element.currentStyle[attrName]
//ie9+
window.getComputedStyle(el,null)[attrName]
window.getComputedStyle(el,null).getPropertyValue(attrName)
//伪类
window.getComputedStyle(el,':after')[attrName]
三、对象
3.1 Object对象
(1)生成实例对象
var o = new Object()
(2)属性
Object.prototype //返回原型对象
(3)方法
Object.keys(o) //遍历对象的可枚举属性
Object.getOwnPropertyName(o) //遍历对象不可枚举的属性
对象实例的方法
valueOf():返回当前对象对应的值。
toString():返回当前对象对应的字符串形式。
toLocaleString():返回当前对象对应的本地字符串形式。
hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
isPrototypeOf():判断当前对象是否为另一个对象的原型。
propertyIsEnumerable():判断某个属性是否可枚举。
3.2 Array对象
(1)生成实例对象
var a = new Array()
(2)属性
a.length //长度
(3)Array.isArray()
Array.isArray(a) //用来判断一个值是否为数组
(4)Array实例的方法

[1, [2, [3, 4]]].toString() // "1,2,3,4"

a.valueof() //返回数组本身
a.toString() //返回数组的字符串形式
a.push(value,vlaue....) //用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。
pop() //用于删除数组的最后一个元素,并返回该元素
join() //以参数作为分隔符,将所有数组成员组成一个字符串返回。如果不提供参数,默认用逗号分隔。
concat() //用于多个数组的合并。它将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不变。
shift() //用于删除数组的第一个元素,并返回该元素。
unshift(value) //用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。
reverse() //用于颠倒数组中元素的顺序,返回改变后的数组
slice(start_index, upto_index); //用于提取原数组的一部分,返回一个新数组,原数组不变。第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员。负数表示倒数第几个。
splice(index, count_to_remove, addElement1, addElement2, ...); //用于删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员,返回值是被删除的元素。第一个参数是删除的起始位置,第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
sort() //对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数,表示按照自定义方法进行排序。该函数本身又接受两个参数,表示进行比较的两个元素。如果返回值大于0,表示第一个元素排在第二个元素后面;其他情况下,都是第一个元素排在第二个元素前面。
map() //对数组的所有成员依次调用一个函数,根据函数结果返回一个新数组。
map(elem,index,arr) //map方法接受一个函数作为参数。该函数调用时,map方法会将其传入三个参数,分别是当前成员、当前位置和数组本身。
forEach() //遍历数组的所有成员,执行某种操作,参数是一个函数。它接受三个参数,分别是当前位置的值、当前位置的编号和整个数组。
filter() //参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。
some() //用来判断数组成员是否符合某种条件。接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。只要有一个数组成员的返回值是true,则整个some方法的返回值就是true,否则false。
every() //用来判断数组成员是否符合某种条件。接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。所有数组成员的返回值都是true,才返回true,否则false。
reduce() //依次处理数组的每个成员,最终累计为一个值。从左到右处理(从第一个成员到最后一个成员)
reduceRight() //依次处理数组的每个成员,最终累计为一个值。从右到左(从最后一个成员到第一个成员)
indexOf(s) //返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1。可以接受第二个参数,表示搜索的开始位置
lastIndexOf() //返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1。
3.3 Number对象
(1)生成对象
var n = new Number()
(2)Number对象的属性
Number.POSITIVE_INFINITY:正的无限,指向Infinity。
Number.NEGATIVE_INFINITY:负的无限,指向-Infinity。
Number.NaN:表示非数值,指向NaN。
Number.MAX_VALUE:表示最大的正数,相应的,最小的负数为-Number.MAX_VALUE。
Number.MIN_VALUE:表示最小的正数(即最接近0的正数,在64位浮点数体系中为5e-324),相应的,最接近0的负数为-Number.MIN_VALUE。
Number.MAX_SAFE_INTEGER:表示能够精确表示的最大整数,即9007199254740991。
Number.MIN_SAFE_INTEGER:表示能够精确表示的最小整数,即-9007199254740991。
(4)Number对象实例的方法
toString() //用来将一个数值转为字符串形式.可以接受一个参数,表示输出的进制。如果省略这个参数,默认将数值先转为十进制,再输出字符串;否则,就根据参数指定的进制,将一个数字转化成某个进制的字符串。
toFixed() //用于将一个数转为指定位数的小数,返回这个小数对应的字符串。
toExponential() //用于将一个数转为科学计数法形式。可传入一个参数,参数表示小数点后有效数字的位数,范围为0到20,超出这个范围,会抛出一个RangeError。
toPrecision() //用于将一个数转为指定位数的有效数字。
3.4 String 对象
(1)生成实例对象
var s = new String()
(2)String对象的属性
s.length //返回字符串的长度
(3)方法
s.chatAt(index) //返回指定位置的字符 //"123456"[0] == "1"
s.fromCharCode() //该方法的参数是一系列Unicode码点,返回对应的字符串。
s.charCodeAt(index) //返回给定位置字符的Unicode码点(十进制表示)
s.concat(s2) //用于连接两个字符串
s.slice(start,end) //用于从原字符串取出子字符串并返回,不改变原字符串。第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置)。如果参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度。
s.substring(start,end) //用于从原字符串取出子字符串并返回,不改变原字符串.第一个参数表示子字符串的开始位置,第二个位置表示结束位置。
s.substr(start,length) //用于从原字符串取出子字符串并返回,不改变原字符串。第一个参数是子字符串的开始位置,第二个参数是子字符串的长度。如果第一个参数是负数,表示倒数计算的字符位置。如果第二个参数是负数,将被自动转为0,因此会返回空字符串。
s.indexOf(s) //返回给定元素在字符串中第一次出现的位置,如果没有出现则返回-1。可以接受第二个参数,表示搜索的开始位置
s.lastIndexOf() //返回给定元素在字符串中最后一次出现的位置,如果没有出现则返回-1。
s.trim() //用于去除字符串两端的空格,返回一个新字符串
s.toLowerCase() //用于将一个字符串全部转为小写,返回一个新字符串,不改变原字符串。
s.toUpperCase() //全部转为大写
s.localeCompare(s2) //用于比较两个字符串。它返回一个整数,如果小于0,表示第一个字符串小于第二个字符串;如果等于0,表示两者相等;如果大于0,表示第一个字符串大于第二个字符串。
s.match(regexp) //用于确定原字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串。如果没有找到匹配,则返回null。
s.search() //返回值为匹配的第一个位置。如果没有找到匹配,则返回-1。
s.replace(oldValue,newValue) //用于替换匹配的子字符串,一般情况下只替换第一个匹配(除非使用带有g修饰符的正则表达式)。
s.split() //按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组。还可传入第二个参数,决定了返回数组的成员数。
3.5 Math对象
(1)属性
Math.E:常数e。
Math.LN2:2的自然对数。
Math.LN10:10的自然对数。
Math.LOG2E:以2为底的e的对数。
Math.LOG10E:以10为底的e的对数。
Math.PI:常数Pi。
Math.SQRT1_2:0.5的平方根。
Math.SQRT2:2的平方根。
(2)数学方法
Math.abs():返回参数的绝对值
Math.ceil():向上取整,接受一个参数,返回大于该参数的最小整数。
Math.floor():向下取整
Math.max(n,n1,...):可接受多个参数,返回最大值
Math.min(n,n1,..):可接受多个参数,返回最小值
Math.pow(n,e):指数运算, 返回以第一个参数为底数、第二个参数为幂的指数值。
Math.sqrt():返回参数值的平方根。如果参数是一个负值,则返回NaN。
Math.log():返回以e为底的自然对数值。
Math.exp():返回e的指数,也就是常数e的参数次方。
Math.round():四舍五入
Math.random():返回0到1之间的一个伪随机数,可能等于0,但是一定小于1。
(3)三角函数方法
Math.sin():返回参数的正弦
Math.cos():返回参数的余弦
Math.tan():返回参数的正切
Math.asin():返回参数的反正弦(弧度值)
Math.acos():返回参数的反余弦(弧度值)
Math.atan():返回参数的反正切(弧度值)
3.6 JSON对象
(1)方法
JSON.stringify()
//用于将一个值转为字符串。该字符串应该符合JSON格式,并且可以被JSON.parse方法还原。
//(JSON.stringify(obj, selectedProperties))还可以接受一个数组,作为第二个参数,指定需要转成字符串的属性。
//还可以接受第三个参数,用于增加返回的JSON字符串的可读性。如果是数字,表示每个属性前面添加的空格(最多不超过10个);如果是字符串(不超过10个字符),则该字符串会添加在每行前面。

JSON.parse() //用于将JSON字符串转化成对象。
3.7 console对象
(1)方法
console.log(text,text2,...) //用于在console窗口输出信息。它可以接受多个参数,将它们的结果连接起来输出。如果第一个参数是格式字符串(使用了格式占位符),console.log方法将依次用后面的参数替换占位符,然后再进行输出。
console.info() //在console窗口输出信息,同时,会在输出信息的前面,加上一个蓝色图标。
console.debug() //在console窗口输出信息,同时,会在输出信息的前面,加上一个蓝色图标。
console.warn() //输出信息时,在最前面加一个黄色三角,表示警告;
console.error() //输出信息时,在最前面加一个红色的叉,表示出错,同时会显示错误发生的堆栈
console.table() //可以将复合类型的数据转为表格显示。
console.count() //用于计数,输出它被调用了多少次。
console.dir() //用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示。
console.dirxml() //用于以目录树的形式,显示DOM节点。
console.assert() //接受两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果。

//这两个方法用于计时,可以算出一个操作所花费的准确时间。
console.time()
console.timeEnd()
//time方法表示计时开始,timeEnd方法表示计时结束。它们的参数是计时器的名称。调用timeEnd方法之后,console窗口会显示“计时器名称: 所耗费的时间”。

console.profile() //用来新建一个性能测试器(profile),它的参数是性能测试器的名字。
console.profileEnd() //用来结束正在运行的性能测试器。

console.group()
console.groupend()
//上面这两个方法用于将显示的信息分组。它只在输出大量信息时有用,分在一组的信息,可以用鼠标折叠/展开。
console.groupCollapsed() //用于将显示的信息分组,该组的内容,在第一次显示时是收起的(collapsed),而不是展开的。

console.trace() //显示当前执行的代码在堆栈中的调用路径。
console.clear() //用于清除当前控制台的所有输出,将光标回置到第一行。


正则表达式:
只允许输入汉字:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')"
只允许输入数字:onkeyup="this.value=this.value.replace(/\D/g,'')"
[1, [2, [3, 4]]].toString() // "1,2,3,4"

setTimeout(fn, 0) 的作用

setTimeout(0)单线程和异步队列
setTimeout和setInterval是JS内置的两个定时器,使用很简单,但这两个方法背后的原理却不简单。
我们知道,JS是单线程语言,在浏览器中,当JS代码被加载时,浏览器会为其分配一个主线程来执行任务(函数),
主线程会形成一个全局执行环境,执行环境采用栈的方式将待执行任务按顺序依次来执行。
但在浏览器中有一些任务是非常耗时的,比如http请求、定时器、事件回调等,为了保证其他任务的执行效率不被影响,
JS在执行环境中维护了一个异步队列(也叫工作线程),并将这些任务放入队列中进行等待,这些任务的执行时机并不确定,
只有当主线程的任务执行完成以后,才会去检查异步队列中的任务是否需要开始执行。这就是为什么setTimeout(fn,0)
始终要等到最后执行的原因。关于单线程和异步队列问题请参考:setTimeout(0)

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}

POST和GET的区别,HTTP状态码


1
2
3
4
5
6
7
8
9
10
POST和GET的区别
GET在浏览器回退时是无害的,而POST会再次提交请求
GET产生的URL地址可以被收藏,而POST不可以
GET请求会被浏览器主动缓存,而POST不会,除非手动设置
GET请求只能进行URL编码,而POST支持多种编码方式
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
GET请求在URL中传送的参数是有长度限制的,而POST没有长度限制
对参数的数据类型,GET只能请求ASCII字符,而POST没有限制
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传敏感信息
GET参数通过URL传递,POST放在Request body中

Post,Get接口傻傻分不清?

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP状态码
1XX:指示信息-表示请求已接受,继续处理
2XX:成功-表示请求已被成功接收200 OK :客户端请求成功
206 Partial Content:客户发送一个带有Range头的GET请求,服务器完成了它 播放视频和音频
3XX:重定向-要完成请求必须进行更进一步的操作301 Move Permanently:所请求的页面已经转移至新的URL
302 Found:所请求的页面已经临时转移到新的URL
304 Not Modified:客户端有缓冲的文档并发出一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
4XX:客户端错误-请求有语法错误或请求无法实现400 Bad Request:客户端请求有语法错误,不能被服务器所理解
401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden:对被请求页面的访问被禁止
404 Not Found:请求资源不存在
5XX:服务错误-服务器未能实现合法的请求500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
503 Server Unavailable:请求未完成,服务器临时过载或当机,一段事件后恢复正常

301和302

301与302:二者都是进行重定向,前者为永久重定向,后者为临时重定向。301,302对用户来说没有区别,他们看到效果只是一个跳转,浏览器中旧的URL变成了新的URL。实际工作中,当我们的前一个域名被永久性停止使用,并且不希望用户还能访问以前的域名时,我们会用到301。
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于:301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了), 搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问), 这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

301重定向和302重定向的区别:

  • 302重定向只是暂时的重定向,搜索引擎会抓取新的内容而保留旧的地址,因为服务器返回302,所以,搜索搜索引擎认为新的网址是暂时的。
  • 而301重定向是永久的重定向,搜索引擎在抓取新的内容的同时也将旧的网址替换为了重定向之后的网址。
1
2
3
4
5
6
7
8
9
10
11
301适合永久重定向
  301比较常用的场景是使用域名跳转。
比如,我们访问 http://www.baidu.com 会跳转到 https://www.baidu.com,
发送请求之后,就会返回301状态码,
然后返回一个location,提示新的地址,浏览器就会拿着这个新的地址去访问。
  注意: 301请求是可以缓存的, 即通过看status code,可以发现后面写着from cache。
  或者你把你的网页的名称从php修改为了html,这个过程中,也会发生永久重定向。

302用来做临时跳转
  比如未登陆的用户访问用户中心重定向到登录页面。
  访问404页面会重新定向到首页。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
301—永久移动。被请求的资源已被永久移动位置;
【永久重定向】
302—请求的资源现在临时从不同的 URI 响应请求;
 【临时重定向】
305—使用代理。被请求的资源必须通过指定的代理才能被访问;

307—临时跳转。被请求的资源在临时从不同的URL响应请求;

400—错误请求;

402—需要付款。该状态码是为了将来可能的需求而预留的,用于一些数字货币或者是微支付;

403—禁止访问。服务器已经理解请求,但是拒绝执行它;

404—找不到对象。请求失败,资源不存在;

406—不可接受的。请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体;

408—请求超时;

409—冲突。由于和被请求的资源的当前状态之间存在冲突,请求无法完成;

410—遗失的。被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址;

413—响应实体太大。服务器拒绝处理当前请求,请求超过服务器所能处理和允许的最大值。

417—期望失败。在请求头 Expect 中指定的预期内容无法被服务器满足;

418—我是一个茶壶。超文本咖啡罐控制协议,但是并没有被实际的HTTP服务器实现;

420—方法失效。

422—不可处理的实体。请求格式正确,但是由于含有语义错误,无法响应;

500—服务器内部错误。服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理;

JS返回所有子节点对象childNodes

1
2
3
4
5
6
7
8
9
10
var mylist = document.getElementById('myid');
for(var i=0,i<mylist.childNodes.length;i++){
console.log(mylist.childNodes[i]);
}
firstChild返回第一个子节点
lastChild返回最后一个子节点
parentNode返回父节点对象
nextSibling返回下一个兄弟节点对象
previousSibling返回前一个兄弟节点对象
nodeName返回节点的HTML标记名称

insertBefore()插入节点(父节点内容的最前面)

注意:insertBefore()有两个参数,第一个是插入的节点,第二个是插入的位置

例子:

1
2
3
var list = document.getElementById('myList');
list.insertBefore(newItem,list.childNodes[1]);
//插入新节点newItem到list的第二个子节点

Element.matches 精确匹配

1
2
3
4
5
6
7
8
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
if (target.matches('li.class-1')) {
console.log('the content is: ', target.innerHTML);
}
});

移动端touch事件(区分webkit 和 winphone)

当用户手指放在移动设备在屏幕上滑动会触发的touch事件,以下支持webkit

1
2
3
4
5
6
7
8
9
10
touchstart——当手指触碰屏幕时候发生。不管当前有多少只手指
touchmove——当手指在屏幕上滑动时连续触发。通常我们再滑屏页面,会调用event的preventDefault()可以阻止默认情况的发生:阻止页面滚动
touchend——当手指离开屏幕时触发
touchcancel——系统停止跟踪触摸时候会触发。例如在触摸过程中突然页面alert()一个提示框,此时会触发该事件,这个事件比较少用TouchEvent
touches:屏幕上所有手指的信息
targetTouches:手指在目标区域的手指信息
changedTouches:最近一次触发该事件的手指信息
touchend时,touches与targetTouches信息会被删除,changedTouches保存的最后一次的信息,最好用于计算手指信息参数信息(changedTouches[0])
clientX、clientY在显示区的坐标
target:当前元素

全局错误监控

监听window上的error事件,过滤事件代理的error。

手动触发一个dom事件,需要3步,如果你对document.createEvent,�不是很熟悉,可以点击查看。

创建一个事件对象 document.createEvent(event)
初始化事件对象 event.initEvent(type, bubbles, true)
分发事件 dom.dispatchEvent(event)

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
Object.preventExtensions(obj)  让一个对象变的不可扩展,也就是永远不能再添加新的属性。
Object.isExtensible(obj) 判断一个对象是否是可扩展的
Object.seal(obj)让一个对象密封(只能读写 不能新增)
Object.isSealed(obj)判断一个对象是否密封
Object.isFrozen(arr) 让一个对象被冻结(只能读)
Object.isFrozen(obj):判断一个对象是否被冻结
Object.keys(obj) 返回一个由给定对象的所有可枚举自身属性的属性名组成的数组
Object.getOwnPropertyNames(obj):返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组
Object.is(value1, value2):判断两个值是否是同一个值,Object.is它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的。
Object.assign 把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。【浅复制】
//var copy = Object.assign({}, obj);
Object.defineProperty() 定义单个对象属性或方法(可以设置读写可枚举)
Object.defineProperties() 定义多个对象属性或方法(可以设置读写可枚举)

Object.assign() //浅拷贝,类似{...obj1,...obj2} 都是浅拷贝
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)

var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

//如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

var obj1 = {a: {b: 1}};
var obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2

对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
var target = { a: { b: 'c', d: 'e' } }
var source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

停止jq中的ajax请求用abort()函数

var currentAjax = null;
functionstartAjax(){
//方法就是将XHR对象指向currentAjax,再调用currentAjax的.abort()来中止请求
currentAjax = $.ajax({
type:'POST',
beforeSend:function(){},
url:'test.php',
data:'username=xxx',
dataType:'JSON',
error:function(){alert('error')},
success:function(data){alert(data)}
});
}
functionstopAjax(){
//如若上一次AJAX请求未完成,则中止请求
if(currentAjax) {currentAjax.abort();}
}



JSONP 超时:

设置超时标志位flag=false,当超时后将flag=true;
异步加载JS文件并执行回调函数:

function loadJS(src, callback){
var script = document.createElement('script');
var head = document.getElementsByTagName('head')[0];
var loaded;
script.src = src;
if(typeof callback === 'function'){
script.onload = script.onreadystatechange = function(){
if(!loaded && (!script.readyState || /loaded|complete/.test(script.readyState))){
script.onload = script.onreadystatechange = null;
loaded = true;
callback();
}
}
}
head.appendChild(script);
}

获取元素的绝对位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function getPosition(node) {
var width = node.offsetWidth; //元素宽度
var height = node.offsetHeight; //元素高度
var left = node.offsetLeft; //获取元素相对于其根元素的left值var left
var top = node.offsetTop; //获取元素相对于其根元素的top值var top
current = node.offsetParent; // 取得元素的offsetParent

// 一直循环直到根元素  
while(current != null) {  
left += current.offsetLeft;  
top += current.offsetTop;  
current = current.offsetParent;  
}
return {
"width": width,
"height": height,
"left": left,
"top": top
};
}

动画结束时事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
o.addEventListener("webkitAnimationEnd", function() {
console.log("动画结束");
})

-webkit-animation动画有三个事件:
开始事件: webkitAnimationStart
结束事件: webkitAnimationEnd
重复运动事件: webkitAnimationIteration// 动画开始时事件


o.addEventListener("webkitAnimationStart", function() {
console.log("动画开始");
})
// 动画重复运动时事件
o.addEventListener("webkitAnimationIteration", function() {
console.log("动画重复运动");
})
// 动画结束时事件
o.addEventListener("webkitAnimationEnd", function() {
console.log("动画结束");
})

解决键盘弹出遮挡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 解决键盘弹出后挡表单的问题
window.addEventListener('resize', function() {
if(
document.activeElement.tagName === 'INPUT' ||
document.activeElement.tagName === 'TEXTAREA'
) {
window.setTimeout(function() {
if('scrollIntoView' in document.activeElement) {
document.activeElement.scrollIntoView();
} else {
document.activeElement.scrollIntoViewIfNeeded();
}
}, 0);
}
});
1
2
3
4
5
6
/**
* 处理iOS 微信客户端6.7.4 键盘收起页面未下移bug
*/
;(/iphone|ipod|ipad/i.test(navigator.appVersion)) && document.addEventListener('blur', (e) => {
document.body.scrollIntoView(false)
}, true)

单个for循环实现排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a = [12, 13, 65, 54, 86, 21, 37, 1, 95, 4];
var l=a.length;
for(var i = 0; i < l; i++) {
if(a[i] > a[i + 1]) {
var tem = a[i];
a[i] = a[i + 1];
a[i + 1] = tem;
}
if(i == l - 1) {
i = -1;
l--;
}
}
console.log(a);

Object.assign实现:

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
if (!Object.assign) {
// 定义assign方法
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) { // assign方法的第一个参数
'use strict';
// 第一个参数为空,则抛错
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}

var to = Object(target);
// 遍历剩余所有参数
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
// 参数为空,则跳过,继续下一个
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);

// 获取改参数的所有key值,并遍历
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
// 如果不为空且可枚举,则直接浅拷贝赋值
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}

JavaScript中巧用位运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
日常前端开发中我们很少用到位运算,容易让人遗忘,让我们一起回顾下一下js中的位运算。
位运算详细说明查看JavaScript|MDN
下面主要回顾一下一些常用的位运算的巧用。
将十进制转化为二进制
var number = 3;
var result = number.toString(2);

var result2 = 14..toString(2); // "1110"


我们使用位运算来代替Math.floor()来向下取整
var data = 2.2352524535;
var result = data | 0; // 2


var re2 = ~~data; // 2

将颜色从RGA转换为Hex格式

1
2
3
4
5
6
7
var color = {r: 186, g: 218, b: 85};

// RGB to HEX
var rgb2hex = function(r, g, b) {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).substr(1);
}
rgb2hex(color.r, color.g, color.b);//"#bada55"

常用函数:

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
/*========================常用函数========================*/

/*时间格式化*/
Date.prototype.Format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, /*月份*/
"d+": this.getDate(), /*日*/
"h+": this.getHours(), /*小时*/
"m+": this.getMinutes(), /*分*/
"s+": this.getSeconds(), /*秒*/
"q+": Math.floor((this.getMonth() + 3) / 3), /*季度*/
"S": this.getMilliseconds() /*毫秒*/
};
if (/(y+)/.test(fmt))
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};

/*IE浏览器不支持date(time),所以用此方法转换*/
function NewDate(fmt) {
/*首先将日期分隔 ,获取到日期部分 和 时间部分*/
var day = fmt.split(' ');
/*获取日期部分的年月日*/
var days = day[0].split('-');
/*获取时间部分的 时分秒*/
var mi = day[day.length - 1].split(':');
/*获取当前date类型日期*/
var date = new Date();
/*给date赋值 年月日*/
date.setUTCFullYear(days[0], days[1] - 1, days[2]);
/*给date赋值 时分秒 首先转换utc时区 :+8*/
date.setUTCHours(mi[0] - 8, mi[1], mi[2]);
return date;
}

/*为空判断*/
function isEmpty(s) {
switch (typeof(s)) {
case 'string':
return !s.length;
break;
case 'array':
case 'object':
for (var i in s) return false;
return true;
break;
case 'undefined':
return true;
break;
default:
return !s;
break;
}
}

/*数字判断*/
function isNumber(s) {
return typeof(s) == 'number' ? true : false;
}

/*整数判断*/
function isInt(s) {
var re = /^-?\d*$/;
return re.test(s);
}

/*正整数判断*/
function isUInt(s) {
var re = /^\d*$/;
return re.test(s) && s >= 0;
}

/*小数判断*/
function isDecimal(s, bit) {
if (!arguments[1]) bit = -1;
if (bit == -1) {
var re = /^-?\d*.?\d*$/;
return re.test(s);
} else {
var re = new RegExp('^-?\\d*.?\\d{0,' + bit + '}$');
return re.test(s);
}
}

/*正小数判断*/
function isUDecimal(s, bit) {
if (!arguments[1]) bit = -1;
if (bit == -1) {
var re = /^\d*.?\d*$/;
return re.test(s) && s >= 0;
} else {
var re = new RegExp('^\\d*.?\\d{0,' + bit + '}$');
return re.test(s) && s >= 0;
}
}

/*字符串判断*/
function isString(s) {
return typeof(s) == 'string';
}

/*========================/常用函数========================*/

js onkeyup replace 自动替换

检测浮点数 只能是整数或者小数
多余的就replace 掉 的表单验证


function checkFloatNum(obj)
{
//先把非数字的都替换掉,除了数字和.
obj.value = obj.value.replace(/[^\d.]/g,"");
//必须保证第一个为数字而不是.
obj.value = obj.value.replace(/^\./g,"");
//保证只有出现一个.而没有多个.
obj.value = obj.value.replace(/\.{2,}/g,".");
//保证.只出现一次,而不能出现两次以上
obj.value = obj.value.replace(".","$#$").replace(/\./g,"").replace("$#$",".");
}

performance

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
;
(function() {

handleAddListener('load', getTiming)

function handleAddListener(type, fn) {
if(window.addEventListener) {
window.addEventListener(type, fn)
} else {
window.attachEvent('on' + type, fn)
}
}

function getTiming() {
try {
var time = performance.timing;
var timingObj = {};

var loadTime = (time.loadEventEnd - time.loadEventStart) / 1000;

if(loadTime < 0) {
setTimeout(function() {
getTiming();
}, 200);
return;
}

timingObj['重定向时间'] = (time.redirectEnd - time.redirectStart) / 1000;
timingObj['DNS解析时间'] = (time.domainLookupEnd - time.domainLookupStart) / 1000;
timingObj['TCP完成握手时间'] = (time.connectEnd - time.connectStart) / 1000;
timingObj['HTTP请求响应完成时间'] = (time.responseEnd - time.requestStart) / 1000;
timingObj['DOM开始加载前所花费时间'] = (time.responseEnd - time.navigationStart) / 1000;
timingObj['DOM加载完成时间'] = (time.domComplete - time.domLoading) / 1000;
timingObj['DOM结构解析完成时间'] = (time.domInteractive - time.domLoading) / 1000;
timingObj['脚本加载时间'] = (time.domContentLoadedEventEnd - time.domContentLoadedEventStart) / 1000;
timingObj['onload事件时间'] = (time.loadEventEnd - time.loadEventStart) / 1000;
timingObj['页面完全加载时间'] = (timingObj['重定向时间'] + timingObj['DNS解析时间'] + timingObj['TCP完成握手时间'] + timingObj['HTTP请求响应完成时间'] + timingObj['DOM结构解析完成时间'] + timingObj['DOM加载完成时间']);

for(item in timingObj) {
console.log(item + ":" + timingObj[item] + '毫秒(ms)');
}

console.log(performance.timing);

} catch(e) {
console.log(timingObj)
console.log(performance.timing);
}
}
})();
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
export default class Cookie {
//写cookies
static setCookie = (name, value) => {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
}
//读取cookies
static getCookie = (name) => {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if(arr = document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}

//删除cookies
static delCookie = (name) => {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval = getCookie(name);
if(cval != null)
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}
}

jsonp

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
    function jsonp(objects){
objects = objects || {};
if(!objects.url || !objects.callback){
throw new Error('参数不合法');
}

//创建script标签并插入
var callbackName = ('jsonp_' + Math.random()).replace(".", "");//随机生成callbackName

var script = document.createElement('script');
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);

window[callbackName] = function (json) {
body.removeChild(script);
clearTimeout(script.timer);
window[callbackName] = null;
objects.callback && objects.callback(json);
};

//发出请求
script.src = objects.url + callbackName;

//响应时间
if(objects.time){
script.timer = setTimeout(function () {
window[callbackName] = null;
body.removeChild(script);
objects.fail && objects.fail('超时');
}, objects.time);
}
}
}

js判断DOM是否包含另一个DOM

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
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
.first{
width: 200px;
height: 200px;
background: red;
}
.first-child{
width: 100px;
height: 100px;
background: green;
}
</style>
</head>

<body>
<div class="first">
<div class="first-child"></div>
</div>
<div class="second">
<div class="second-child">
<div class="second-child-child"></div>
</div>
</div>
<script type="text/javascript">
var first = document.querySelector('.first');
var firstchild = document.querySelector('.first-child');

var second = document.querySelector('.second');
var secondchild = document.querySelector('.second-child');
var secondchildchild = document.querySelector('.second-child-child');



function isChildOf(child, parent) {
var parentNode;
if(child && parent) {
parentNode = child.parentNode;
while(parentNode) {
if(parent === parentNode) {
return true;
}
parentNode = parentNode.parentNode;
}
}
return false;
}

console.log(isChildOf(firstchild, first));
console.log(isChildOf(secondchild, second));
console.log(isChildOf(secondchildchild, secondchild));
console.log(isChildOf(secondchildchild, second));

console.log(isChildOf(secondchildchild, first));

console.log(document.documentElement.contains(document.body));
console.log(second.contains(secondchildchild));


</script>
</body>

</html>

回到顶部带动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* 滚动条动画:
* 移动端:document.body.scrollTop
PC端:document.documentElement.scrollTop




//使用:gotoTop(400,callBack)
gotoTop(durations, callback = undefined) {
const doc = document.documentElement
const scrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
for (var i = 60; i >= 0; i--) {
setTimeout((i => {
return () => {
doc.scrollTop = scrollTop * i / 60
if (i === 0 && typeof callback === 'function') {
callback()
}
}
})(i), durations * (1 - i / 60))
}
}




* */
var requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var cancelAnimationFrame = (function() {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
function(id) {
window.clearTimeout(id)
}
})()

// Tween中的方法接受4个参数t,b,c,d 。t为初始时间 b、c、d三个参数(即初始值,变化量,持续时间)。返回值为当前位置
// t => time(初始记步次数) b => begin(初始位置) c => change(变化量) d => duration(持续次数)

var tween = {
linear: function(t, b, c, d) {
return c * t / d + b;
},
easeIn: function(t, b, c, d) {
return c * (t /= d) * t + b;
},
strongEaseIn: function(t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
},
strongEaseOut: function(t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
},
sineaseIn: function(t, b, c, d) {
return c * (t /= d) * t * t + b;
},
sineaseOut: function(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
},
easeInOutQuad: function(t, b, c, d) {
t /= d / 2;
if(t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
}
};
var myReq;
export var animatedScrollTo = function(element, to, duration, callback, Bzr = 'linear') {
var start = element.scrollTop,
change = to - start,
animationStart = +new Date();
var animating = true;
var lastpos = null;

var animateScroll = function() {
if(!animating) {
return;
}
myReq=requestAnimFrame(animateScroll);
var now = +new Date();
var val = Math.floor(tween[Bzr](now - animationStart, start, change, duration));

if(lastpos) {
if(lastpos === element.scrollTop) {
lastpos = val;
element.scrollTop = val;
} else {
animating = false;
}
} else {
lastpos = val;
element.scrollTop = val;
}
if(now > animationStart + duration) {
element.scrollTop = to;
animating = false;
cancelAnimationFrame(myReq); //清除定时器动画
callback&&callback();
}
};
myReq=requestAnimFrame(animateScroll);
};

使用js唤起Native下的App

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
/**
* 浏览器的相关信息
*/
var Browser = /** @class */ (function() {
function Browser() {}
/**
* 获取浏览器数据
*/
Browser.getBrowser = function() {
var UA = navigator.userAgent || ''
var isAndroid = (function() {
return UA.match(/Android/i) ? true : false
})()
var isQQ = (function() {
return /(iPad|iPhone|iPod).*? (IPad)?QQ\/([\d\.]+)/.test(UA) || /\bV1_AND_SQI?_([\d\.]+)(.*? QQ\/([\d\.]+))?/.test(UA)
})()
var isIOS = (function() {
return UA.match(/iPhone|iPad|iPod/i) ? true : false
})()
var isSafari = (function() {
return /iPhone|iPad|iPod\/([\w.]+).*(safari).*/i.test(UA)
})()
var isWx = (function() {
return UA.match(/micromessenger/i) ? true : false
})()
var isWb = (function() {
return UA.match(/weibo/i) ? true : false
})()
var isAndroidChrome = (function() {
return(UA.match(/Chrome\/([\d.]+)/) || UA.match(/CriOS\/([\d.]+)/)) && isAndroid && !isQQ
})()
var isQZ = (function() {
return UA.indexOf('Qzone/') !== -1
})()
var browser = {
isAndroid: isAndroid,
isIOS: isIOS,
isSafari: isSafari,
isQQ: isQQ,
isWb: isWb,
isWx: isWx,
isQZ: isQZ,
isAndroidChrome: isAndroidChrome
}
return browser
}
return Browser
})()

/**
* AppLink:H5唤起APP的所有方法
*/
var AppLink = /** @class */ (function() {
/**
* 类的contructor方法
* @param config IAppLink类型的config文件
*/
function AppLink(config) {
/**
* UA
*/
this.UA = {}
/**
* 传入的config数据,以接口约束
*/
this.config = {}
this.UA = navigator.userAgent || ''
this.config = config
}
/**
* 跳转函数
* @param url 链接
*/
AppLink.prototype.go = function(url) {
window.location.href = url
}
/**
* 检查是否唤起
* @param cb 回调函数
*/
AppLink.prototype.checkOpen = function(cb) {
var inter = null
var statue = false
var count = 0
inter = window.setInterval(function() {
count++
statue = document.hidden || document.webkitHidden
if(statue || count > 40) {
cb(statue)
clearInterval(inter)
}
}, 50)
}
/**
* 外部调用的入口函数
*/
AppLink.prototype.open = function() {
var _this = this
var browser = Browser.getBrowser()
var config = this.config
// 微信直接跳 应用宝
if(browser.isWx) {
this.go(this.config.yyb)
} else if(browser.isQQ) {
if(browser.isIOS) {
// 没有下载
this.checkOpen(function(isSuccess) {
if(!isSuccess) {
_this.go(_this.config.appstore)
}
})
}
// iOS跳到AppStore
if(browser.isAndroid) {
// 使用scheme唤起
this.tryCallApp(this.config.schema)
// 唤起失败 跳到应用宝
this.checkOpen(function(isSuccess) {
if(!isSuccess) {
_this.go(_this.config.yyb)
}
})
}
} else if(browser.isWb) {
// 使用scheme唤起
this.tryCallApp(this.config.schema)
// 微博:唤起失败,也不跳转,会有引导功能
} else if(browser.isSafari) {
var version = this.getIOSVersion()
// iOS10以下不支持直接跳转到AppStore,跳到应用宝
if(version < 10) {
this.go(this.config.yyb)
} else {
this.go(this.config.appstore)
}
} else {
// 其他情况,直接跳应用宝
this.go(this.config.yyb)
}
}
/**
* 下载按钮的url地址
*/
AppLink.prototype.getDownloadUrl = function() {
var browser = Browser.getBrowser()
var config = this.config
var url = ''
if(browser.isQQ) {
if(browser.isIOS) {
url = this.config.appstore
} else {
url = this.config.yyb
}
} else if(browser.isSafari) {
var version = this.getIOSVersion()
if(version < 10) {
url = this.config.yyb
} else {
url = this.config.appstore
}
} else if(browser.isWb) {
url = ''
} else {
url = this.config.yyb
}
return url
}
/**
* 尝试唤起APP
* @param scheme 唤起的scheme
*/
AppLink.prototype.tryCallApp = function(scheme) {
var aLink = document.createElement('a'),
body = document.body
aLink.href = scheme
body.appendChild(aLink)
aLink.click()
}
/**
* 判断iOS版本
*/
AppLink.prototype.getIOSVersion = function() {
var ver = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/)
var version = parseInt(ver[1], 10)
return version
}
return AppLink
})()

高阶函数

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
<script type="text/javascript">
function add(...x) {
var sum = x.reduce((a,b)=>a+b,0)
var tmp = function(...y) {
sum =sum+y.reduce((a,b)=>a+b,0)
return tmp;
};
tmp.toString = function() {
return sum;
};
return tmp;
}

console.log(+add(1)(2)(3)) // 6
console.log(+add(1)(2,3)) // 6
console.log(+add(1,2)(3)) // 6
console.log(+add(1)(2)(3,4)) //10


add(1)(2)(3).valueOf
//ƒ valueOf() { [native code] }
add(1)(2)(3).valueOf()
//ƒ 6
+add(1)(2)(3).valueOf()
//6
+""+add(1)(2)(3).valueOf()
//6
</script>

add(2)(3)(),add(2,3)()输出5

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
function sum() {
var cur = Array.prototype.slice.call(arguments).reduce(function(a, b) {
return a + b;
}, 0);

function add() {
return arguments.length == 0 ? cur : (cur += Array.prototype.slice.call(arguments).reduce(function(a, b) {
return a + b
}, 0), add);
};
return arguments.length == 0 ? 0 : add;
</script>

获取CSS样式

1
2
3
4
5
6
7
function getStyle(obj, attr) {
if(obj.currentStyle) {
return obj.currentStyle[attr]; //IE678
} else {
return window.getComputedStyle(obj, null)[attr];
}
}

Colors

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
function rgbToRgba(rgbValue) {
const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
return rgb ? `rgba(${rgb[1]},1)` : rgbValue;
}

function hexToRgba(hexValue) {
const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b);
const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
const r = parseInt(rgb[1], 16);
const g = parseInt(rgb[2], 16);
const b = parseInt(rgb[3], 16);
return `rgba(${r},${g},${b},1)`;
}

function hslToRgba(hslValue) {
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
const h = parseInt(hsl[1]) / 360;
const s = parseInt(hsl[2]) / 100;
const l = parseInt(hsl[3]) / 100;
const a = hsl[4] || 1;

function hue2rgb(p, q, t) {
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1 / 6) return p + (q - p) * 6 * t;
if(t < 1 / 2) return q;
if(t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
let r, g, b;
if(s == 0) {
r = g = b = l;
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return `rgba(${r * 255},${g * 255},${b * 255},${a})`;
}

function colorToRgb(val) {
if(is.rgb(val)) return rgbToRgba(val);
if(is.hex(val)) return hexToRgba(val);
if(is.hsl(val)) return hslToRgba(val);
}

750rem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function(doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function() {
var clientWidth = docEl.clientWidth;
if(!clientWidth) return;
if(clientWidth >= 750) {
docEl.style.fontSize = '100px';
} else {
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
};
if(!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

表单验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static checkType = (str, type) => {
switch(type) {
case 'email':
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
case 'phone':
return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
case 'tel':
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
case 'number':
return /^[0-9]$/.test(str);
case 'english':
return /^[a-zA-Z]+$/.test(str);
case 'chinese':
return /^[\u4E00-\u9FA5]+$/.test(str);
case 'lower':
return /^[a-z]+$/.test(str);
case 'upper':
return /^[A-Z]+$/.test(str);
default:
return true;
}
}
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
class navigatorUtil {
/**
* 头部信息
* @type {[type]}
*/
static userAgent = navigator.userAgent;

/**
* 是否为ipad
*/
static isIPad = () => {
return(navigatorUtil.userAgent.indexOf("iPad") > -1);
}

/**
* 是否为iphone
* @param {[type]} userAgent [description]
* @return {Boolean} [description]
*/
static isIPhone = () => {
return(navigatorUtil.userAgent.indexOf("iPhone") > -1);
}

/**
* 是否为ios系统
* @param {[type]} userAgent [description]
* @return {Boolean} [description]
*/

static isIOS = () => {
return navigatorUtil.isIPad(navigatorUtil.userAgent) || navigatorUtil.isIPhone(
navigatorUtil.userAgent);
}

/**
* 是否为Android系统
* @param {[type]} userAgent [description]
* @return {Boolean} [description]
*/
static isAndroid = () => {
return(navigatorUtil.userAgent.indexOf("Android") > -1) ||
(navigatorUtil.userAgent.indexOf("Linux") > -1);
}

/**
* 判断是否为微信
*/
static isWeixin = () => {
return(navigatorUtil.userAgent.indexOf("MicroMessenger") > -1);
}

/**
*
* @desc 获取浏览器类型和版本
* @return {String}
*/
static getExplore = () => {
var sys = {},
ua = navigator.userAgent.toLowerCase(),
s;
(s = ua.match(/rv:([\d.]+)\) like gecko/)) ? sys.ie = s[1]:
(s = ua.match(/msie ([\d\.]+)/)) ? sys.ie = s[1] :
(s = ua.match(/edge\/([\d\.]+)/)) ? sys.edge = s[1] :
(s = ua.match(/firefox\/([\d\.]+)/)) ? sys.firefox = s[1] :
(s = ua.match(/(?:opera|opr).([\d\.]+)/)) ? sys.opera = s[1] :
(s = ua.match(/chrome\/([\d\.]+)/)) ? sys.chrome = s[1] :
(s = ua.match(/version\/([\d\.]+).*safari/)) ? sys.safari = s[1] : 0;
// 根据关系进行判断 if (sys.ie) return ('IE: ' + sys.ie)
if(sys.edge) return('EDGE: ' + sys.edge)
if(sys.firefox) return('Firefox: ' + sys.firefox)
if(sys.chrome) return('Chrome: ' + sys.chrome)
if(sys.opera) return('Opera: ' + sys.opera)
if(sys.safari) return('Safari: ' + sys.safari)
return 'Unkonwn'
}

/**
*
* @desc 获取操作系统类型
* @return {String}
*/
static getOS = () => {
var userAgent = 'navigator' in window && 'userAgent' in navigator && navigator.userAgent.toLowerCase() || '';
var vendor = 'navigator' in window && 'vendor' in navigator && navigator.vendor.toLowerCase() || '';
var appVersion = 'navigator' in window && 'appVersion' in navigator && navigator.appVersion.toLowerCase() || '';

if(/mac/i.test(appVersion)) return 'MacOSX'
if(/win/i.test(appVersion)) return 'windows'
if(/linux/i.test(appVersion)) return 'linux'
if(/iphone/i.test(userAgent) || /ipad/i.test(userAgent) || /ipod/i.test(userAgent)) 'ios'
if(/android/i.test(userAgent)) return 'android'
if(/win/i.test(appVersion) && /phone/i.test(userAgent)) return 'windowsPhone'
}

//进入全屏模式, 判断各种浏览器,找到正确的方法
static launchFullScreen = (element) => {
if(element.requestFullscreen) {
element.requestFullscreen();
} else if(element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if(element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if(element.msRequestFullscreen) {
element.msRequestFullscreen();
}
return true;
}
//退出全屏模式
static exitFullScreen = () => {
if(document.exitFullscreen) {
document.exitFullscreen();
} else if(document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if(document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
return false;
}

}

promise

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
function Promise(task) {
let self = this; //缓存this
self.status = 'pending'; //默认状态为pending
self.value = undefined; //存放着此promise的结果
self.onResolvedCallbacks = []; //存放着所有成功的回调函数
self.onRejectedCallbacks = []; //存放着所有的失败的回调函数

// 调用resolve方法可以把promise状态变成成功态
function resolve(value) {
if(value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(function() { // 异步执行所有的回调函数
// 如果当前状态是初始态(pending),则转成成功态
// 此处这个写判断的原因是因为resolved和rejected两个状态只能由pending转化而来,两者不能相互转化
if(self.status == 'pending') {
self.value = value;
self.status = 'resolved';
self.onResolvedCallbacks.forEach(item => item(self.value));
}
});

}

// 调用reject方法可以把当前的promise状态变成失败态
function reject(value) {
setTimeout(function() {
if(self.status == 'pending') {
self.value = value;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(item => item(value));
}
});
}

// 立即执行传入的任务
try {
task(resolve, reject);
} catch(e) {
reject(e);
}
}

/**
* resolvePromise函数的目的是与原生Promise相兼容,可以互相调用
*/
function resolvePromise(promise2, x, resolve, reject) {
// 将返回的promise不停的调用执行,直到失败或者返回一个普通的数据类型
if(promise2 === x) {
return reject(new TypeError('循环引用'));
}
let then, called;

if(x != null && ((typeof x == 'object' || isFunction(x)))) {
// promise
try {
then = x.then;
if(isFunction(then)) {
then.call(x, function(value) {
if(called) return;
called = true;
resolvePromise(promise2, value, resolve, reject);
}, function(reason) {
if(called) return;
called = true;
reject(reason);
});
} else {
resolve(x);
}
} catch(e) {
if(called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}

/**
* onFulfilled成功的回调,onReject失败的回调
*/
Promise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
// 当调用时没有写函数给它一个默认函数值
onFulfilled = isFunction(onFulfilled) ? onFulfilled : function(value) {
return value
};
onRejected = isFunction(onRejected) ? onRejected : function(value) {
throw value
};
let promise2;
if(self.status == 'resolved') {
promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if(self.status == 'rejected') {
promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
let x = onRejected(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if(self.status == 'pending') {
promise2 = new Promise(function(resolve, reject) {
self.onResolvedCallbacks.push(function(value) {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
self.onRejectedCallbacks.push(function(value) {
try {
let x = onRejected(value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
return promise2;
}

/**
* catch实际上是then的一个简写,成功回调传空值即可
*/
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
}

/**
* Promise.reject(error)是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。
*/
Promise.resolve = function(value) {
return new Promise(function(resolve, reject) {
if(typeof value !== null && typeof value === 'object' && isFunction(value.then)) {
value.then();
} else {
resolve(value);
}
})
};

Promise.reject = function(reason) {
return new Promise(function(resolve, reject) {
reject(reason);
})
};

/**
* all方法,可以传入多个promise,全部执行完后会将结果以数组的方式返回,如果有一个失败就返回失败
*/
Promise.all = function(promises) {
return new Promise(function(resolve, reject) {
let result = []; // all方法最终返回的结果
let count = 0; // 完成的数量
for(let i = 0; i < promises.length; i++) {
promises[i].then(function(data) {
result[i] = data;
if(++count == promises.length) {
resolve(result);
}
}, function(err) {
reject(err);
});
}
});
}

/**
* race方法,可以传入多个promise,返回的是第一个执行完的resolve的结果,如果有一个失败就返回失败
*/
Promise.race = function(promises) {
return new Promise(function(resolve, reject) {
for(let i = 0; i < promises.length; i++) {
promises[i].then(function(data) {
resolve(data);
}, function(err) {
reject(err);
});
}
});
}

Promise.deferred = Promise.defer = function() {
var defer = {};
defer.promise = new Promise(function(resolve, reject) {
defer.resolve = resolve;
defer.reject = reject;
})
return defer;
}

/**
* 一些会多次使用的复用功能函数
*/
function isFunction(obj) {
return typeof obj === "function";
}

/**
* 最后可以通过以下命令安装一个promises测试插件,用插件来测试自己实现的promise符不符合规范
* npm(cnpm) i -g promises-aplus-tests
* promises-aplus-tests Promise.js
*/

module.exports = Promise;

ES6 promise

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
class es6Promise {
constructor(task) {
let self = this; //缓存this
self.status = 'pending'; //默认状态为pending
self.value = undefined; //存放着此promise的结果
self.onResolvedCallbacks = []; //存放着所有成功的回调函数
self.onRejectedCallbacks = []; //存放着所有的失败的回调函数

// 调用resolve方法可以把promise状态变成成功态
function resolve(value) {
if(value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => { // 异步执行所有的回调函数
// 如果当前状态是初始态(pending),则转成成功态
// 此处这个写判断的原因是因为resolved和rejected两个状态只能由pending转化而来,两者不能相互转化
if(self.status == 'pending') {
self.value = value;
self.status = 'resolved';
self.onResolvedCallbacks.forEach(item => item(self.value));
}
});

}

// 调用reject方法可以把当前的promise状态变成失败态
function reject(value) {
setTimeout(() => {
if(self.status == 'pending') {
self.value = value;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(item => item(value));
}
});
}

// 立即执行传入的任务
try {
task(resolve, reject);
} catch(e) {
reject(e);
}
}

/**
* onFulfilled成功的回调,onReject失败的回调
* 原型链方法
*/
then(onFulfilled, onRejected) {
let self = this;
// 当调用时没有写函数给它一个默认函数值
onFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value;
onRejected = isFunction(onRejected) ? onRejected : value => {
throw value
};
let promise2;
if(self.status == 'resolved') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if(self.status == 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if(self.status == 'pending') {
promise2 = new Promise((resolve, reject) => {
self.onResolvedCallbacks.push(value => {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
self.onRejectedCallbacks.push(value => {
try {
let x = onRejected(value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
return promise2;
}

/**
* catch实际上是then的一个简写,成功回调传空值即可
* 原型链方法
*/
catch(onRejected) {
return this.then(null, onRejected);
}

/**
* Promise.reject(err)是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。
* 静态方法为类自己的方法,不在原型链上
*/
static resolve(value) {
return new Promise((resolve, reject) => {
if(typeof value !== null && typeof value === 'object' && isFunction(value.then)) {
value.then();
} else {
resolve(value);
}
})
}

static reject(err) {
return new Promise((resolve, reject) => {
reject(err);
})
}

/**
* all方法,可以传入多个promise,全部执行完后会将结果以数组的方式返回,如果有一个失败就返回失败
* 静态方法为类自己的方法,不在原型链上
*/
static all(promises) {
return new Promise((resolve, reject) => {
let result = []; // all方法最终返回的结果
let count = 0; // 完成的数量
for(let i = 0; i < promises.length; i++) {
promises[i].then(data => {
result[i] = data;
if(++count == promises.length) {
resolve(result);
}
}, err => {
reject(err);
});
}
});
}

/**
* race方法,可以传入多个promise,返回的是第一个执行完的resolve的结果,如果有一个失败就返回失败
* 静态方法为类自己的方法,不在原型链上
*/
static race(promises) {
return new Promise((resolve, reject) => {
for(let i = 0; i < promises.length; i++) {
promises[i].then(data => {
resolve(data);
}, err => {
reject(err);
});
}
});
}
}

/**
* resolvePromise函数的目的是与原生Promise相兼容,可以互相调用
*/
function resolvePromise(promise2, x, resolve, reject) {
// 将返回的promise不停的调用执行,直到失败或者返回一个普通的数据类型
if(promise2 === x) {
return reject(new TypeError('循环引用'));
}
let then;
let called;

if(x != null && ((typeof x == 'object' || isFunction(x)))) {
// promise
try {
then = x.then;
if(isFunction(then)) {
then.call(x, value => {
if(called) return;
called = true;
resolvePromise(promise2, value, resolve, reject);
}, reason => {
if(called) return;
called = true;
reject(reason);
});
} else {
resolve(x);
}
} catch(e) {
if(called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}

Promise.deferred = Promise.defer = () => {
const defer = {};
defer.promise = new Promise((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
})
return defer;
}

/**
* 一些会多次使用的复用功能函数
*/
function isFunction(obj) {
return typeof obj === "function";
}

/**
* 最后可以通过以下命令安装一个promises测试插件,用插件来测试自己实现的promise符不符合规范
* npm(cnpm) i -g promises-aplus-tests
* promises-aplus-tests es6Promise.js
*/

module.exports = es6Promise;

使用promise手动封装ajax函数

跨浏览器实现Ajax兼容

1
2
3
4
5
6
7
8
9
10
11
var createXMLHttpRequest = function () {
if ( window.XMLHttpRequest ) {
return new XMLHttpRequest();
}
else if ( window.ActiveXObject ) {
return new ActiveXObject('Microsoft.XMLHttp');
}
else {
return;
}
}

Ajax的xhr对象有如下常用API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
onloadstart                     开始send触发
onprogress 从服务器上下载数据每50ms触发一次,判断进度
onload 得到响应
onerror 服务器异常
open xhr.open('GET', url, true) 请求方式,请求url, true(默认)异步请求
onloadend 请求结束,无论成功失败
onreadystatechange xhr.readyState改变使触发
onabort 调用xhr.abort时触发
ontimeout 超时触发
setRequestHeader 设置RequestHeader,eg:xhr.setRequestHeader('Accept','application/json');
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.getAllResponseHeaders() 返回全部头信息,string
getResponseHeader 获取Response头信息,eg:xhr.getResponseHeader('date');
xhr.getResponseHeader('content-type');

readyState上面都是静态函数。而readyState表示了请求的状态:

  • value=0,open()方法还未被调用
  • value=1,open()调用,send()方法还未被调用
  • value=2,send()方法已经被调用,响应头和响应状态已经返回
  • value=3,响应体下载中,responseText中已经获取了部分数据
  • value=4,请求完成,整个请求过程已经完毕。

GET

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getJSON (url) {
return new Promise( (resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
//if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304))
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(this.responseText, this)
} else {
var resJson = { code: this.status, response: this.response }
reject(resJson, this)
}
}
}
xhr.send()
})
}

POST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function postJSON(url, data) {
return new Promise( (resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open("POST", url, true)
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
//if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304))
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(this.responseText), this)
} else {
var resJson = { code: this.status, response: this.response }
reject(resJson, this)
}
}
}
xhr.send(JSON.stringify(data))
})
}
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
// ajax函数的默认参数
var ajaxOptions = {
url: '#',
method: 'GET',
async: true,
timeout: 0,
data: null,
dataType: 'text',
headers: {},
onprogress: function () { },
onuploadprogress: function () { },
xhr: null
}

/**
* ajax函数,返回一个promise对象
* @param {Object} optionsOverride 参数设置,支持的参数如下
* url: url地址,默认"#"
* method: 请求方法,仅支持GET,POST,默认GET
* async: 是否异步,默认true
* timeout: 请求时限,超时将在promise中调用reject函数
* data: 发送的数据,该函数不支持处理数据,将会直接发送
* dataType: 接受的数据的类型,默认为text
* headers: 一个对象,包含请求头信息
* onprogress: 处理onprogress的函数
* ouploadprogress: 处理.upload.onprogress的函数
* xhr: 允许在函数外部创建xhr对象传入,但必须不能是使用过的

* @return {Promise}
* 该函数注册xhr.onloadend回调函数,判断xhr.status是否属于 [200,300)&&304 ,
* 如果属于则promise引发resolve状态,允许拿到xhr对象
* 如果不属于,或已经引发了ontimeout,onabort,则引发reject状态,允许拿到xhr对象
*
* 关于reject
* 返回一个对象,包含
* errorType:错误类型,
* abort_error: xhr对象调用abort函数
* timeout_error: 请求超时
* onerror: xhr对象触发了onerror事件
* send_error: 发送请求出现错误
* status_error: 响应状态不属于 [200,300)&&304
*/
function ajax(optionsOverride) {
// 将传入的参数与默认设置合并
var options = {};
for (var k in ajaxOptions) {
options[k] = optionsOverride[k] || ajaxOptions[k];
}
options.async = options.async === false ? false : true;
var xhr = options.xhr = options.xhr || new XMLHttpRequest();

return new Promise(function (resolve, reject) {
xhr.open(options.method, options.url, options.async);
xhr.timeout = options.timeout;

//设置请求头
for (var k in options.headers) {
xhr.setRuquestHeader(k, options.headers[k]);
}

// 注册xhr对象事件
xhr.onprogress = options.onprogress;
xhr.upload.onprogress = options.onuploadprogress;
xhr.responseType = options.dataType;

xhr.onabort = function () {
reject(new Error({
errorType: 'abort_error',
xhr: xhr
}));
}
xhr.ontimeout = function () {
reject({
errorType: 'timeout_error',
xhr: xhr
});
}
xhr.onerror = function () {
reject({
errorType: 'onerror',
xhr: xhr
})
}
xhr.onloadend = function () {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)
resolve(xhr);
else{
reject({
errorType: 'status_error',
xhr: xhr
})
}
}

try {
xhr.send(options.data);
}
catch (e) {
reject({
errorType: 'send_error',
error: e
});
}
})
}

二分查找 【二分查找也称折半查找 ,时间复杂度:O(log2n)】

非递归算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function binary_search(arr,key){
var low=0,
high=arr.length-1;
while(low<=high){
var mid=parseInt((high+low)/2);
if(key==arr[mid]){
return mid;
}else if(key>arr[mid]){
low=mid+1;
}else if(key<arr[mid]){
high=mid-1;
}else{
return -1;
}
}
};
var arr=[1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result=binary_search(arr,10);
alert(result); // 9 返回目标元素的索引值

递归算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function binary_search(arr,low,high,key){
if(low>high){
return -1;
}
var mid=parseInt((high+low)/2);
if(arr[mid]==key){
return mid;
}else if(arr[mid]>key){
high=mid-1;
return binary_search(arr,low,high,key);
}else if(arr[mid]<key){
low=mid+1;
return binary_search(arr,low,high,key);
}
};
var arr=[1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result=binary_search(arr,0,13,10);
alert(result); // 9 返回目标元素的索引值

排序

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = i+1; j < len -1; j++) {
arr[i] > arr[j] ? [arr[i],arr[j]]= [arr[j],arr[i]]:null;
}
}
return arr;
}

bubbleSort([1,2,4,7,2,8,0,9,11,32,17])
[0, 1, 2, 2, 4, 7, 8, 9, 11, 32, 17]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function bubble_sort(arr){
for(var i=0;i<arr.length-1;i++){
for(var j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
var swap=arr[j];
arr[j]=arr[j+1];
arr[j+1]=swap;
}
}
}
}

var arr=[3,1,5,7,2,4,9,6,10,8];
bubble_sort(arr);
console.log(arr);

快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function quick_sort(arr){
if(arr.length<=1){
return arr;
}
var pivotIndex=Math.floor(arr.length/2);
var pivot=arr.splice(pivotIndex,1)[0];

var left=[];
var right=[];
for(var i=0;i<arr.length;i++){
if(arr[i]<pivot){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}

return quick_sort(left).concat([pivot],quick_sort(right));
}

var arr=[5,6,2,1,3,8,7,1,2,3,4,7];
console.log(quick_sort(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function qSort(arr) {
if (arr.length==0) {
return [];
}
var left = [];
var right = [];
var pivot = arr[0];
for (var i =1; i <arr.length; i++) { // 注意这里的起始值,因为有一个作为flag了
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return qSort(left).concat(pivot, qSort(right));
}

当我们 new 一个类的时候 都发生了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* new2 new关键字的代码实现演示
* @param {function} func 被new的类 (构造函数)
*/
function new2(func) {
// 创建了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象
let o = Object.create(func.prototype);
// (在构造函数中this指向当前实例)让这个类作为普通函数值行 并且里面this为实例对象
let k = func.call(o);
// 最后再将实例对象返回 如果你在类中显示指定返回值k,
// 注意如果返回的是引用类型则将默认返回的实例对象o替代掉
return typeof k === 'object' ? k : o;
}

// 实验
functionM() { // 即将被new的类
this.name = 'liwenli';
}

let m = new2(M); // 等价于 new M 这里只是模拟
console.log(m instanceof M); // instanceof 检测实例
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);

new操作符的工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
废话不多说,直接上代码
var newObj = function(func){
var t = {}
t.prototype = func.prototype
var o = t
var k =func.call(o);
if(typeof k === 'object'){
return k;
}else{
return o;
}
}
var parent1 = newObj(Parent)等价于new操作

1.一个新对象被创建,它继承自func.prototype。
2.构造函数func 被执行,执行的时候,相应的参数会被传入,同时上下文(this) 会被指定为这个新实例。
3.如果构造函数返回了一个新对象,那么这个对象会取代整个new出来的结果,如果构造函数没有返回对象,
那么new出来的结果为步骤1创建的对象。

Object.create 兼容实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let obj1 = {id: 1};
Object._create = (o) => {
let Fn = function() {}; // 临时的构造函数
Fn.prototype = o;
return new Fn;
}

let obj2 = Object._create(obj1);
console.log(obj2.__proto__ === obj1); // true
console.log(obj2.id); // 1

// 原生的Object.create
let obj3 = Object.create(obj1);
console.log(obj3.__proto__ === obj1); // true
console.log(obj3.id); // 1

curry 将多参数函数转换为接收单一参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let fn = function(a, b, c) { // 多参数函数
return a + b + c;
}
function curry(fn) {
let args = []; // 收集参数
let len = fn.length;
returnfunctionfe() {
args = args.concat([].slice.call(arguments, 0));
if (args.length === len) {
return fn.apply(null, args);
}
return fe;
}
}

console.log(curry(fn)(1)(2)(3)); // 6

函数节流

throttle 策略的电梯。保证如果电梯第一个人进来后,50毫秒后准时运送一次,不等待。如果没有人,则待机。

1
2
3
4
5
6
7
8
9
10
let throttle = (fn, delay = 50) => { // 节流 控制执行间隔时间 防止频繁触发 scroll resize mousemove
let stattime = 0;
returnfunction (...args) {
let curTime = new Date();
if (curTime - stattime >= delay) {
fn.apply(this, args);
stattime = curTime;
}
}
}

防抖动

debounce 策略的电梯。如果电梯里有人进来,等待50毫秒。如果又人进来,50毫秒等待重新计时,直到50毫秒超时,开始运送。

1
2
3
4
5
6
7
8
let debounce = (fn, time = 50) => { // 防抖动 控制空闲时间 用户输入频繁
let timer;
returnfunction (...args) {
let that = this;
clearTimeout(timer);
timer = setTimeout(fn.bind(that, ...args), time);
}
}

Function的bind实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype._bind = function(context) {
let func = this;
let params = [].slice.call(arguments, 1);
returnfunction() {
params = params.concat([].slice.call(arguments, 0));
func.apply(context, params);
}
}

let obj = {id: 24}

function fn1(a) {
console.log(this, arguments);
}
let foo = fn1._bind(obj, obj.id);

函数组合串联compose(koa reduce中间件)

1
2
3
4
5
6
7
8
9
10
11
// 组合串联
let fn1 = (a) => a + 1;
let fn2 = (b) => b + 2;
let fn3 = (c) => c + 3;

let funs = [fn1, fn2, fn3];

let compose = (func) => {
return arg => func.reduceRight((composed, fn) => fn(composed), arg);
}
console.log(compose(funs)(100)); // 相当于fn1(fn2(fn3(100)))

co函数

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
function* fn(a) {
a = yield a;
let b = yield 2;
let c = yield 3;
return a + b + c;
}


function co(fn, ...args) {
let g = fn(...args);
return new Promise((resolve, reject) => {
function next(lastValue) {
let { value, done } = g.next(lastValue);
if (done) {
resolve(value);
} else {
if (value instanceof Promise) {
value.then(next, (val) => reject(val));
} else {
next(value)
}
}
}
next();
});
}
co(fn, 100).then(value => {
console.log(value); // 105
});

如何主动中止Promise调用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const p1 = new Promise((resolve, reject) => {
setTimeout(() => { // 异步操作
resolve('start')
}, 1000);
});

p1.then((result) => {
console.log('a', result);
return Promise.reject('中断后续调用'); // 此时rejected的状态将直接跳到catch里,剩下的调用不会再继续
}).then(result => {
console.log('b', result);
}).then(result => {
console.log('c', result);
}).catch(err => {
console.log(err);
});

// a start
// 中断后续调用

window.requestAnimationFrame兼容性处理

1
2
3
4
5
6
7
8
window._requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();

字符串是否符合回文规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let str = 'My age is 0, 0 si ega ym.';

方法一
function palindrome(params) {
params = params.replace(/[\W\s_]/ig, '');
return params.toLowerCase() === params.split('').reverse().join('').toLowerCase();
}
console.log(palindrome(str));

方法二
function palindrome(params) {
params = params.replace(/[\W\s_]/ig, '').toLowerCase();
for (var i = 0, j = params.length-1; i<j; i++, j--) {
if (params[i] !== params[j]) {
returnfalse;
}
}
returntrue;
}

解构:将 destructuringArray([1, [2, 3], 4], “[a, [b], c]”) => {a: 1, b: 2, c: 4}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 将 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4}
const targetArray = [1, [2, 3], 4];
const formater = "[a, [b], c]";

const destructuringArray = (values, keys) => {
try {
const obj = {};
if (typeof keys === 'string') {
keys = JSON.parse(keys.replace(/\w+/g, '"$&"'));
}

const iterate = (values, keys) =>
keys.forEach((key, i) => {
if(Array.isArray(key)) iterate(values[i], key)
else obj[key] = values[i]
})

iterate(values, keys)

return obj;
} catch (e) {
console.error(e.message);
}
}

数组展平

1
2
3
4
5
6
7
8
将[[1, 2], 3, [[[4], 5]]] 展平为 [1, 2, 3, 4, 5]

let arr = [[1, 2], 3, [[[4], 5]]]; // 数组展平
function flatten(arr) {
return [].concat(
...arr.map(x => Array.isArray(x) ? flatten(x) : x)
)
}

找出数组中重复出现过的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 例如:[1,2,4,4,3,3,1,5,3]
// 输出:[1,3,4]
let arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];

// 方法一
function repeat1(arr){
var result = [], map = {};
arr.map(function(num){
if(map[num] === 1) result.push(num); // 等于1说明之前出现过一次 这次重复出现了
map[num] = (map[num] || 0) + 1; // 微妙之处 开始第一次出现无值 记为 0 + 1 = 1 下一次从1开始累加
});
return result;
}
console.log(repeat1(arr));

// 方法二

function repeat(arr) {
let result = arr.filter((x, i, self) => {
return self.indexOf(x) === i && self.lastIndexOf(x) !== i
}); //
return result;
}
console.log(repeat(arr));

将数组中按照数字重复出现的次数进行排序

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
// 如果次数相同 则按照值排序 比如  2, 2, 2和 1, 1, 1  应排序为 [1, 1, 1, 2, 2, 2]
// 比如 [1,2,1,2,1,3,4,5,4,5,5,2,2] => [3, 4, 4, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2]

let arr = [9, 7, 7, 1, 2, 1, 2, 1, 3, 4, 5, 4, 5, 5, 2, 2];
function sortArray(arr) {
let obj = {};
let newArr = [];
for(let i = 0; i < arr.length; i++) {
let cur = arr[i];
if(obj[cur]){
obj[cur].push(cur);
continue;
}
obj[cur] = [cur];
}
for(let k in obj) {
if(obj.hasOwnProperty(k)) {
newArr.push(obj[k])
}
}
newArr.sort((a, b) => {
if(a.length === b.length){
return a[0] - b[0];
}
return a.length - b.length;
});
newArr = newArr.reduce((prev, cur) => prev.concat(cur));
return newArr;
}
console.log(sortArray(arr)); // [ 3, 9, 4, 4, 7, 7, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2 ]

移动端关闭虚拟键盘

1
document.activeElement.blur();

解决setInterval在浏览器切换中的问题

1
2
3
4
5
6
7
document.onvisibilitychange=function(){
if(document.visibilityState=="visible"){
timer=setInterval(slidemove, 1000);
}else{
clearInterval(timer);
}
}

Promise 透传

1
2
3
Promise.resolve(1).then(2).then((a)=>console.log(a))
1
Promise {<resolved>: undefined}

不用循环,创建一个长度为 100 的数组,并且每个元素的值等于它的下标

1
2
3
4
5
6
es6版:
Array.from(Array(100).keys())
或者
[...Array(100).keys()]

Array(100).fill(0).map((t,i)=>i)

箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

异步加载的区别

  • defer是在HTML解析完之后才会执行,如果是多个,按照加载的顺序依次执行
  • async是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关
    async和defer

html 触底的条件是:

1
2
document.documentElement.scrollHeight ===
document.documentElement.clientHeight + document.documentElement.scrollTop

雅虎十四条优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. 尽可能的减少 HTTP 的请求数	content
2. 使用 CDN(Content Delivery Network) server
3. 添加 Expires 头(或者 Cache-control ) server
4. Gzip 组件 server
5. 将 CSS 样式放在页面的上方 css
6. 将脚本移动到底部(包括内联的) javascript
7. 避免使用 CSS 中的 Expressions css
8. 将 JavaScript 和 CSS 独立成外部文件 javascript css
9. 减少 DNS 查询 content
10. 压缩 JavaScript 和 CSS (包括内联的) javascript css
11. 避免重定向 server
12. 移除重复的脚本 javascript
13. 配置实体标签(ETags) css
14. 使 AJAX 缓存
15. DNS预读取 <link rel="dns-prefetch" href="https://data.dadaabc.com/">
16. 多域名分发静态资源 同域下浏览器能并发的请求有限,为了增加并发,尤其是一些静态资源上,可以使用多个域名。
17. 最小化重排和重绘
18. 函数防抖和函数节流

微任务有两种 nextTick和then 那么这两个谁快呢?

1
2
3
4
5
6
7
8
Promise.resolve('123').then(res=>{
console.log(res);

})
process.nextTick(() => console.log('nextTick'))

//顺序 nextTick 123
//很明显 nextTick快

setTimeout第三个参数

1
2
3
setTimeout(function(...list){
console.log(list); // [1, 2, 3, 4, 5]
},100,1,2,3,4,5)

Object和Function之间最让人琢磨不透的

1
2
3
4
5
6
7
8
9
10
11
12
Object instanceof Object // true
Object instanceof Function // true
Function instanceof Function // true
Function instanceof Object // true

const o = {}
o instanceof Object //true
o instanceof Function // false

function F () {}
F instanceof Object //true
F instanceof Function //true

文件上传进度提示使用Javascript的XMLHttpRequest的progress事件

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
使用Javascript的XMLHttpRequest的progress事件,实现示例代码为:

var formData = new FormData();
formData.append("file", document.getElementById('file').files[0]);
formData.append("token", token_value); // 其他参数按这样子加入

var xhr = new XMLHttpRequest();
xhr.open('POST', '/uploadurl');
// 上传完成后的回调函数
xhr.onload = function () {
if (xhr.status === 200) {
  console.log('上传成功');
} else {
 console.log('上传出错');
}
};
// 获取上传进度
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var percent = Math.floor(event.loaded / event.total * 100) ;
// 设置进度显示
$("#J_upload_progress").progress('set progress', percent);
}
};
xhr.send(formData);

面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script type="text/javascript">
function A(obj) {
obj.s = "哈"
obj = { //obj 重新赋值,并且是一个对象,此时obj指向a的指针指向这个赋值对象
"name": 2,
"city": "亚洲"
}
return obj
}
var a = {
"name": 1,
"city": "中国"
}
var b = A(a);
console.log(b); //{name: 2, city: "亚洲"}
console.log(a); //{name: 1, city: "中国", s: "哈"}
</script>

LESS和SASS

语言 实现 特性 赋值 缩进
Sass Ruby 变量$开头 $var: value 不需要
Less JavaSript 变量@开头 @var: value 不需要
Stylus NodeJs 不能使用@开头 var:10 都可以

创造纯净的对象

1
2
3
4
5
6
7
你可以创造100%纯净的对象,它不会从Object类继承任何方法(例如:构造函数、toString() 等)。

const pureObject =Object.create(null);
console.log(pureObject);//=> {}
console.log(pureObject.constructor);//=> undefined
console.log(pureObject.toString);//=> undefined
console.log(pureObject.hasOwnProperty);//=> undefined

将一个任意长的数字变成逗号分割的格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
例子:

// 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
答:

functionparseToMoney(num) {
num = parseFloat(num.toFixed(3));
let [integer, decimal] = String.prototype.split.call(num, '.');
integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
return integer + '.' + (decimal ? decimal : '');
}

检测对象中属性的存在

使用in关键字。

该方法可以判断对象的自有属性和继承来的属性是否存在。

1
2
3
4
var o={x:1};
"x" in o; //true,自有属性存在
"y" in o; //false
"toString" in o; //true,是一个继承属性

使用对象的hasOwnProperty()方法。

该方法只能判断自有属性是否存在,对于继承属性会返回false。

1
2
3
4
var o={x:1};
o.hasOwnProperty("x");    //true,自有属性中有x
o.hasOwnProperty("y");    //false,自有属性中不存在y
o.hasOwnProperty("toString"); //false,这是一个继承属性,但不是自有属性

n!

1
2
3
4
5
6
7
functionfactorial(number){
if(number==1) {
return number;
} else{
return number*factorial(number-1);
}
}

斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, … 求第n个数是多少

1
2
3
4
5
6
functionfibonacci(number) {
if (number <=2) {
return1;
}
returnfibonacci(number-1) +fibonacci(number -2)
}

服务端推送的几种方式

服务端推送是现今Web开发过程中最常见的需求。例如:

  • 即时聊天工具
  • H5网络游戏
  • 消息通知

一般的服务器推送包括:

  • 最简单的是客户端轮询的方式,在客户端创建一个定时器,每隔一定的时间去请求服务端,每次请求检查状态变化以判断服务端是否有新数据更新。
  • 基于 AJAX 的长轮询(long-polling)方式,服务器在一段时间后再返回信息;
  • HTTP Streaming,通过iframe<script>标签完成数据的传输;
  • TCP长连接/WebSocket,可以实现服务器主动发送数据至网页端,它和HTTP一样,是一个基于HTTP的应用层协议,跑的是TCP,所以本质上还是个长连接,双向通信,意味着服务器端和客户端可以同时发送并响应请求,而不再像HTTP的请求和响应
  • SSE: Server-Sent Events,【webpack】这是通过http协议变通实现的,通过服务端向客户端声明,接下来是要发送的是流信息,本质上就是完成一次耗时长的下载。

JavaScript进阶提高必读

JavaScript常用七种继承方案

JS 面试原生常用api 方法大全

JS代码手写题

PM2来部署nodejs服务器永久开启

JavaScript设计模式

URL 解析

JavaScript专题

数据结构

轻松理解JavaScript原型及原型链

H5 移动调试全攻略

JS 面试原生常用api 方法大全

javascript 原生常用API大全
















preload、prefetch

现在的网络情况虽然很乐观,但是

defer和async

当浏览器碰到 script 脚本的时候:

<script src="script.js"></script>

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

<script async src="script.js"></script>

有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

<script defer src="myscript.js"></script>

有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

然后从实用角度来说呢,首先把所有脚本都丢到 之前是最佳实践,因为对于旧浏览器来说这是唯一的优化选择,此法可保证非脚本的其他一切元素能够以最快的速度得到加载和解析。

接着,我们来看一张图咯:

此图告诉我们以下几个要点:

  1. defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)

  2. 它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的

  3. 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用

  4. async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行

  5. 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,

preload和refetch

preload通常在页面中,我们需要加载一些脚本和样式,而使用 preload 可以对当前页面所需的脚本、样式等资源进行预加载,而无需等到解析到 script 和 link 标签时才进行加载。这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能。

使用方式
将 link 标签的 rel 属性的值设为 preload,as 属性的值为资源类型(如脚本为 script,样式表为 style)

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
 <head>

<meta charset="utf-8">

<title>
preload example
</title>

<!-- 对 style.css 和 index.js 进行预加载 -->

<link rel="preload" href="style.css" as="style">

<link rel="preload" href="index.js" as="script">

<link rel="stylesheet" href="style.css">

</head>

<body>

<div id="app"></div>

<script src="index.js"></script>

</body>
prefetchpreload 一样,都是对资源进行预加载,但是 prefetch 加载的资源一般不是用于当前页面的,即未来很可能用到的这样一些资源,简单点说就是其他页面会用到的资源。当然,prefetch 不会像 preload 一样,在页面渲染的时候加载资源,而是利用浏览器空闲时间来下载。当进入下一页面,就可直接从 disk cache 里面取,既不影响当前页面的渲染,又提高了其他页面加载渲染的速度。

使用方式
preload 很相似,无需指定 as 属性:

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
 <head>

<meta charset="utf-8">

<title>
preload example
</title>

<!-- 对 style.css 和 index.js 进行 preload 预加载 -->

<link rel="preload" href="style.css" as="style">

<link rel="preload" href="index.js" as="script">

<!-- 对资源进行 prefetch 预加载 -->

<link rel="prefetch" href="next.css">

<link rel="prefetch" href="next.js">

<link rel="stylesheet" href="style.css">

</head>

<body>

<div id="app"></div>

<script src="index.js"></script>

</body>

总结:对当前页面需要的资源,使用 preload 进行预加载,对其它页面需要的资源进行 prefetch 预加载。

Subresource和Prerender

subresource可以用来指定资源是最高优先级的。比如,在Chrome和Opera中我们可以加上下面的代码:

<link rel="subresource" href="styles.css">

Chromium的文档这么解释:
和 “Link rel=prefetch”的语义不同,”Link rel=subresource”是一种新的连接关系。rel=prefetch指定了下载后续页面用到资源的低优先级,而rel=subresource则是指定当前页面资源的提前加载。

所以,如果资源是在当前页面需要,或者马上就会用到,则推荐用subresource,否则还是用prefetch。

prerender是一个重量级的选项,它可以让浏览器提前加载指定页面的所有资源。

<link rel="prerender" href="/thenextpage.html" />

Steve Souders的文章详细解释了这个技术:
prerender就像是在后台打开了一个隐藏的tab,会下载所有的资源、创建DOM、渲染页面、执行JS等等。如果用户进入指定的链接,隐藏的这个页面就会进入马上进入用户的视线。Google Search多年前就利用了这个特性实现了Instant Pages功能。微软最近也宣布会让Bing在IE11上用类似prerender的技术。

Axios拦截器

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
import axios from 'axios'
import { throwErr } from '@/utils' //utils 捕捉服务端http状态码的方法
import store from '@/store' //引入vuex的相关操作
import { Message } from 'element-ui' //element Toast的提示
import router from '@/router'

//过滤请求
axios.interceptors.request.use(config => {
//config 为请求的一些配置 例如:请求头 请求时间 Token 可以根据自己的项目需求个性化配置,参考axios的中文说明手册 自己多动动手
//由于我们项目的后端大大给力,很多东西在服务端帮我们处理好了所以请求阶段只要传好参数就好了
config.timeout = 10 * 1000 //请求响应时间
return config
}, error => {
return Promise.reject(error)
})
// 添加响应拦截器
axios.interceptors.response.use(
response => {
if (response.data.code === 0) { //服务端定义的响应code码为0时请求成功
return Promise.resolve(response.data) //使用Promise.resolve 正常响应
} else if (response.data.code === 1401) { //服务端定义的响应code码为1401时为未登录
store.dispatch('setUserInfo', {})
Message({
message: '未登录'
})
// router.push('/login')
return Promise.reject(response.data) //使用Promise.reject 抛出错误和异常
} else {
return Promise.reject(response.data)
}
},
error => {
if (error && error.response) {
let res = {}
res.code = error.response.status
res.msg = throwErr(error.response.status, error.response) //throwErr 捕捉服务端的http状态码 定义在utils工具类的方法
return Promise.reject(res)
}
return Promise.reject(error)
}
)
export default function request(method, url, data) { //暴露 request 给我们好API 管理
method = method.toLocaleLowerCase() //封装RESTful API的各种请求方式 以 post get delete为例
if (method === 'post') {
return axios.post(url, data) //axios的post 默认转化为json格式
} else if (method === 'get') {
return axios.get(url, {
params: data
})
} else if (method === 'delete') {
return axios.delete(url, {
params: data
})
}
}

*JavaScript的值传递和引用传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function changeAgeAndReference(person) {
person.age = 25;
person = {
name: 'John',
age: 50
};

return person;
}
var personObj1 = {
name: 'Alex',
age: 30
};
var personObj2 = changeAgeAndReference(personObj1);
console.log(personObj1); // -> {name: "Alex", age: 25}
console.log(personObj2); // -> {name: "John", age: 50}

JSONP 【JSONP百度搜索框】

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
<!DOCTYPE html>
<html>

<head>
<title></title>
<meta charset="utf-8" />
<style type="text/css">
ul,
li {
margin: 0;
padding: 0;
}

.search {
display: block;
margin: 0 auto;
height: 22px;
position: relative;
}


.search-out {
position: absolute;
width: 200px;
height: 300px;
z-index: 10;
top: 23px;
color: #7e8c8d;
overflow-x: hidden;
overflow-y: auto;
margin-top: 5px;
}

.search-out ul li {
font-size: 12px;
height: 25px;
/*li溢出省略*/
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.search-out ul li:hover {
background-color: #edf0f1;
}
</style>
<script type="text/javascript">
window.onload = function() {

let input = document.getElementById('input'),
body = document.getElementsByClassName('search')[0],
btn = document.getElementsByTagName("button")[0],
ul = document.getElementById('ul');
var globalData = ['历史1', '历史2']; //globalData数据,存储历史搜索

input.addEventListener('focus', function() {
var htmlText = '';

for(var i = 0; i < globalData.length; i++) {
htmlText += '<li>' + globalData[i] + '</li>';
}
ul.innerHTML = htmlText;
ul.style.border = "solid 1px #dee6e8";
ul.style.background = "white";
});
btn.addEventListener("click", function() { //当点击搜索按钮把输入框数据添加至历史数组
if(input.value) {
globalData.push(input.value);
}
});

input.addEventListener('blur', function() { //离开输入框隐藏历史下拉列表
ul.style.border = "";
ul.style.background = "";
ul.innerHTML = '';

});

//监听input事件
input.addEventListener('input', function() {
console.log(input.value)
if(!input.value) {
var htmlText = '';

for(var i = 0; i < globalData.length; i++) {
htmlText += '<li>' + globalData[i] + '</li>';
}
ul.innerHTML = htmlText;
ul.style.border = "solid 1px #dee6e8";
ul.style.background = "white";

return false;
}

var val = encodeURI(input.value);
ul.innerHTML = ''; //清空上一次请求所插入的li
jsonp({
url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=' + val + '&cb=',
time: 3000,
callback: function(json) {
var htmlText = '';
for(var i = 0; i < json.s.length; i++) {
htmlText += '<li>' + json['s'][i] + '</li>';
}
ul.innerHTML = htmlText;
},
fail: function(mes) {
alert(mes);
}
});
});

function jsonp(objects) {
objects = objects || {};
if(!objects.url || !objects.callback) {
throw new Error('参数不合法');
}
//创建script标签并插入
var callbackName = ('jsonp_' + Math.random()).replace(".", ""); //随机生成callbackName
var script = document.createElement('script');
var body = document.getElementsByClassName('search')[0];
body.appendChild(script);
window[callbackName] = function(json) {
body.removeChild(script);
clearTimeout(script.timer);
window[callbackName] = null;
objects.callback && objects.callback(json);
};

//发出请求
script.src = objects.url + callbackName;

//响应时间
if(objects.time) {
script.timer = setTimeout(function() {
window[callbackName] = null;
body.removeChild(script);
objects.fail && objects.fail('超时');
}, objects.time);
}
}
}
</script>
</head>

<body>
<div class="search">
<input id="input" type="text" placeholder="搜索材质">
<span>
<button>搜索</button>
</span>
<div class="search-out">
<ul id="ul"></ul>
</div>
</div>

</body>

</html>

Math.round(),Math.ceil(),Math.floor()的区别

1.Math.round():根据“round”的字面意思“附近、周围”,可以猜测该函数是求一个附近的整数,看下面几个例子就明白。

1
2
3
4
5
6
7
8
9
10
11
12
小数点后第一位<5
正数:Math.round(11.46)=11
负数:Math.round(-11.46)=-11

小数点后第一位>5
正数:Math.round(11.68)=12
负数:Math.round(-11.68)=-12

小数点后第一位=5
正数:Math.round(11.5)=12
负数:Math.round(-11.5)=-11
总结:(小数点后第一位)大于五全部加,等于五正数加,小于五全不加。

2.Math.ceil():根据“ceil”的字面意思“天花板”去理解;
例如:

1
2
Math.ceil(11.46)=Math.ceil(11.68)=Math.ceil(11.5)=12
Math.ceil(-11.46)=Math.ceil(-11.68)=Math.ceil(-11.5)=-11

3.Math.floor():根据“floor”的字面意思“地板”去理解;
例如:

1
2
Math.ceil(11.46)=Math.ceil(11.68)=Math.ceil(11.5)=11
Math.ceil(-11.46)=Math.ceil(-11.68)=Math.ceil(-11.5)=-12

Math.random()函数生成n到m间的随机数字

Math.random()函数返回0和1之间的伪随机数,可能为0,但总是小于1,[0,1)

生成n-m,包含n但不包含m的整数:

1
2
3
4
5
6
7
第一步算出 m-n的值,假设等于w

第二步Math.random()*w

第三步Math.random()*w+n

第四步parseInt(Math.random()*w+n, 10)

生成n-m,不包含n但包含m的整数:

1
2
3
4
5
6
7
第一步算出 m-n的值,假设等于w

第二步Math.random()*w

第三步Math.random()*w+n

第四步Math.floor(Math.random()*w+n) + 1

生成n-m,不包含n和m的整数:

1
2
3
4
5
6
7
第一步算出 m-n-2的值,假设等于w

第二步Math.random()*w

第三步Math.random()*w+n +1

第四步Math.round(Math.random()*w+n+1) 或者 Math.ceil(Math.random()*w+n+1)

生成n-m,包含n和m的随机数:

1
2
3
4
5
6
7
第一步算出 m-n的值,假设等于w

第二步Math.random()*w

第三步Math.random()*w+n

第四步Math.round(Math.random()*w+n) 或者 Math.ceil(Math.random()*w+n)

ES6事件总线 EventBus

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

class EventBus {

constructor() {
this.events = {};
}

on(type, callback, scope, ...args) {
if (typeof this.events[type] == "undefined") {
this.events[type] = [];
}
this.events[type].push({scope: scope, callback: callback, args: args});
}

off(type, callback, scope) {
if (typeof this.events[type] == "undefined") {
return;
}
let numOfCallbacks = this.events[type].length;
let newArray = [];
for (let i = 0; i < numOfCallbacks; i++) {
let event = this.events[type][i];
if (event.scope == scope && event.callback == callback) {

} else {
newArray.push(event);
}
}
this.events[type] = newArray;
}

has(type, callback, scope) {
if (typeof this.events[type] == "undefined") {
return false;
}
let numOfCallbacks = this.events[type].length;
if (callback === undefined && scope === undefined) {
return numOfCallbacks > 0;
}
for (let i = 0; i < numOfCallbacks; i++) {
let event = this.events[type][i];
if ((scope ? event.scope == scope : true) && event.callback == callback) {
return true;
}
}
return false;
}

emit(type, target, ...args) {
if (typeof this.events[type] == "undefined") {
return;
}
let bag = {
type: type,
target: target
};
args = [bag].concat(args);
let events = this.events[type].slice();
let numOfCallbacks = events.length;
for (let i = 0; i < numOfCallbacks; i++) {
let event = events[i];
if (event && event.callback) {
let concatArgs = args.concat(event.args);
event.callback.apply(event.scope, concatArgs);
}
}
}

debug() {
let str = "";
for (let type in this.events) {
let numOfCallbacks = this.events[type].length;
for (let i = 0; i < numOfCallbacks; i++) {
let event = this.events[type][i];
let className = "Anonymous";
if (event.scope) {
if (event.scope.constructor.name) {
className = event.scope.constructor.name;
}
}
str += `${className} listening for "${type}"\n`;
}
}
return str;
}

};

export default new EventBus();
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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>EventBus Examples</title>
</head>
<body>

<script type="module">
import EventBus from '/src/eventbus.js';
// Simple example
{
function myHandler(event) {
console.log("myHandler / type: " + event.type);
}
EventBus.on("my_event", myHandler);
EventBus.emit("my_event");
}
// Keeping the scope
{
class TestClass1 {
constructor() {
this.className = "TestClass1";
EventBus.on("callback_event", this.callback, this);
}
callback(event) {
console.log(this.className + " / type: " + event.type + " / dispatcher: " + event.target.className);
}
}
class TestClass2 {
constructor() {
this.className = "TestClass2";
}
dispatch() {
EventBus.emit("callback_event", this);
}
}
let t1 = new TestClass1();
let t2 = new TestClass2();
t2.dispatch();
}
// Passing additional parameters
{
class TestClass1 {
constructor() {
this.className = "TestClass1";
EventBus.on("custom_event", this.doSomething, this);
}
doSomething(event, param1, param2) {
console.log(this.className + ".doSomething");
console.log("type=" + event.type);
console.log("params=" + param1 + param2);
console.log("coming from=" + event.target.className);
}
}
class TestClass2 {
constructor() {
this.className = "TestClass2";
}
ready() {
EventBus.emit("custom_event", this, "javascript events", " are really useful");
}
}
let t1 = new TestClass1();
let t2 = new TestClass2();
t2.ready();
}
// Debugging
console.log(EventBus.debug());
// Removing a registered handler
{
var handler = function() {
console.log('example callback');
};
EventBus.on('EXAMPLE_EVENT', handler);
EventBus.emit('EXAMPLE_EVENT');
EventBus.off('EXAMPLE_EVENT', handler);
// Not emitted because event was removed
EventBus.emit('EXAMPLE_EVENT');
}
</script>
</body>
</html>

JS全排列 【全排列是一种时间复杂度为:O(n!)的算法】

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
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<script type="text/javascript">
//实现两数交换
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}

//递归实现数组全排列并打印
function permutation(arr, index) {
if(index == arr.length) { //全排列结束
console.log(arr);
} else {
for(var i = index; i < arr.length; i++) {
//将第i个元素交换至当前index下标处
swap(arr, index, i);
//以递归的方式对剩下元素进行全排列
permutation(arr, index + 1);
//将第i个元素交换回原处
swap(arr, index, i);
}
}
}

function main() {
var arr = [1, 2, 3]
permutation(arr, 0);
}
main();
</script>
</body>

</html>

一年半经验,百度、有赞、阿里面试总结

2018 大厂高级前端面试题汇总

Google Chrome可跨域模式

1
open -a 'Google Chrome' --args --disable-web-security  --user-data-dir

v-for 循环 index的传值问题

1
2
3
4
5
6
<el-submenu :index="index" v-for="(item,index) in menuList">
<el-menu-item :index="index"
v-for="(subItem,subindex) in item.subMenuList">
{{index}}-{{subItem.subMenuName}}</el-menu-item>

</el-submenu>

发现子组件获取到的index一直都是undefined。

修改办法:

1
2
3
4
<el-menu-item :index="''+index" 
v-for="(subItem,subindex) in item.subMenuList">
{{index}}-{{subItem.subMenuName}}
</el-menu-item>

将 :index 的制改为’’+index,一定是单引号’’ ,子组件获取的到的就变成字符串”0,””1”…..

将字符串”0”变成整数 +”0” 即可!

.vue文件的一个小细节

top.vue

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
<template>
<div class="ds">
<div class="left">
<p>SX</p>
</div>
<div class="right">
<p>SB</p>
</div>
</div>
</template>

<script>
console.log("-------------------------"); //只执行一次


export default {
props: [],
data() {
return {}
},
components: {

},
methods: {

},
created() {

},
mounted() {
console.log("$$$$$$$$$$$$$$$$$$$$$$$"); //执行多次
}
};
</script>

<style lang="scss" scoped="scoped">
.guize {
box-sizing: border-box;
padding: 0 0.4rem;
margin-top: -0.3rem;
width: 100%;
height: 1.2rem;
font-size: 0.24rem;
color: white;
position: relative;
.left {
color: #abc4ed;
float: left;
transform:scale(0.85);
transform-origin:0 0;
p {
font-size: 0.24rem;
line-height: 2;
}
}
.right {
float: left;
width: 1.6rem;
height: 0.44rem;
line-height: 0.44rem;
border: 1px solid white;
color: white;
border-radius: 0.24rem;
text-align: center;
position: absolute;
right: 0.4rem;
bottom: 0.04rem;
}
}
</style>


由浏览器端判断是否支持WebP格式

1
2
3
if(document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0){
// 该浏览器支持WebP格式的图片
}

src转img

function srctoimg(src){
  return new Promise((reslove,reject)=>{
    let img = new Image()
    img.onload = function(){
      reslove(img)
    }
    img.onerror = function(err) {
      reject(err)
    }
    img.src = src
  })
}

img转canvas

function imgtocanvas(img){
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext('2d')
  canvas.width = img.width
  canvas.height = img.height
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  return canvas
}

ImageData转canvas

function ImageDatetocanvas(imgData){
 let canvas = document.createElement("canvas");
 let ctx = canvas.getContext('2d')
 canvas.width = imgData.width
 canvas.height = imgData.height
 ctx.putImageData(imgData,canvas.width, canvas.height);
 return canvas
}

canvas转ImageData

function canvastoImageDate(canvas){
 let ctx = canvas.getContext('2d')
 return ctx.createImageData(canvas.width,canvas.height)
}

canvas像素操作

function canvaspixel(canvas,deal) {
  let ctx = canvas.getContext('2d')
  var imgData = ctx.createImageData(canvas.width, canvas.height);
  for (var i = 0; i < imgData.data.length; i += 4) {
    deal(r,g,b,a)
  }
  ctx.putImageData(imgData, canvas.width, canvas.height);
  return canvas
}

canava转DataURL(base64)

canvas.toDataURL()

DataURL(base64)转blob

function dataURLtoBlob(dataurl) {
  var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}

file转arrayBuffer

function filetoblob(file){
  return new Promise((resolve, reject) => {
    var reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = function (e) {
      resolve(reader.result)
    }
  })
}

file转blob

function filetoblob(file){
  return new Promise((resolve, reject) => {
    var reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = function (e) {
      resolve(new Blob([reader.result],{type:file.type}))
    }
  })
}

(blob,arraybuffer)转file

function blobtofile(blob,name){
    return new File([blob], name ,{type:blob.type})
}

file(blob)转DataURL(base64)

function filetoblob(file) {
  return new Promise((resolve, reject) => {
    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function (e) {
      resolve(reader.result)
    }
    reader.onerror = function (e) {
      resolve(reader.result)
    }
  })
}

dataURL转File

function dataURLtoFile(dataurl, filename) {
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while(n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {type:mime});
}

blob转objectURL

window.URL.createObjectURL(blob)

objectURL转img

srctoimg(src)

objectURL(url)转blob

functionURLtoblob(){
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest()
      xhr.open('GET', input)
      xhr.responseType = 'blob'
      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(xhr.response)
        } else {
          reject(xhr.statusText)
        }
      }
      xhr.onerror = () => reject(xhr.statusText)
      xhr.send()
    })
  }
}

objectURL(url)转arraybuffer,当服务端传递二级制数据时使用

functionURLtoblob(){
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest()
      xhr.open('GET', input)
      xhr.responseType = 'arraybuffer'
      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(xhr.response)
        } else {
          reject(xhr.statusText)
        }
      }
      xhr.onerror = () => reject(xhr.statusText)
      xhr.send()
    })
  }
}

FormData设置blob

function appendBlob(blob){
 var fd = new FormData();
 fd.append("image", blob, "image.png");
 return fd
}

Uint8ClampedArray Uint8Array 区别

Uint8ClampedArray 
 1 ,它会将负数归入0,大于255的数归入255,所以取模就不用了。
 2 ,小数取整
Uint8Array
 1,Uint8Array([-23]) 等价于 new Uint8Array([ 233 ]) 
 2,四舍五入
在处理0-255无区别

arraybuffer,视图(Uint8Array、Float64Array等)之slice

buf返回buf
视图返回视图
1,分配内存
2,拷贝数据

数据

1,new ArrayBuffer(40)
2,new Uint8Array( [ 1, 2, 3, 4 ] )
3,Array.from(uint8Array);

视图参数

var v3 = new Int16Array(buf, 2, 2);
第一个参数:视图对应的底层ArrayBuffer对象,该参数是必需的。
第二个参数:视图开始的字节序号,默认从0开始。
第三个参数:视图包含的数据个数,默认直到本段内存区域结束。

视图.buffer 获取缓冲区

视图对象DataView

var 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);

setInt8:写入1个字节的8位整数。
setUint8:写入1个字节的8位无符号整数。
setInt16:写入2个字节的16位整数。
setUint16:写入2个字节的16位无符号整数。
setInt32:写入4个字节的32位整数。
setUint32:写入4个字节的32位无符号整数。
setFloat32:写入4个字节的32位浮点数。
setFloat64:写入8个字节的64位浮点数。

// 在第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);

实现atob(string 转 base64) window.atob

function _atob(s) {
  var base64hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  s = s.replace(/\s|=/g, '');
  var cur,
    prev,
    mod,
    i = 0,
    result = [];
  while (i < s.length) {
    cur = base64hash.indexOf(s.charAt(i));
    mod = i % 4;
    switch (mod) {
      case 0:
        //TODO
        break;
      case 1:
        result.push(String.fromCharCode(prev << 2 | cur >> 4));
        break;
      case 2:
        result.push(String.fromCharCode((prev & 0x0f) << 4 | cur >> 2));
        break;
      case 3:
        result.push(String.fromCharCode((prev & 3) << 6 | cur));
        break;
    }
    prev = cur;
    i++;
  }
  return result.join('');
}

实现btoa(base64 转 string) window.btoa

function _btoa(s) {
  var base64hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  if (/([^\u0000-\u00ff])/.test(s)) {
    throw new Error('INVALID_CHARACTER_ERR');
  }
  var i = 0,
    prev,
    ascii,
    mod,
    result = [];
  while (i < s.length) {
    ascii = s.charCodeAt(i);
    mod = i % 3;
    switch (mod) {
      // 第一个6位只需要让8位二进制右移两位
      case 0:
        result.push(base64hash.charAt(ascii >> 2));
        break;
      //第二个6位 = 第一个8位的后两位 + 第二个8位的前4位
      case 1:
        result.push(base64hash.charAt((prev & 3) << 4 | (ascii >> 4)));
        break;
      //第三个6位 = 第二个8位的后4位 + 第三个8位的前2位
      //第4个6位 = 第三个8位的后6位
      case 2:
        result.push(base64hash.charAt((prev & 0x0f) << 2 | (ascii >> 6)));
        result.push(base64hash.charAt(ascii & 0x3f));
        break;
    }
    prev = ascii;
    i++;
  }
  // 循环结束后看mod, 为0 证明需补3个6位,第一个为最后一个8位的最后两位后面补4个0。另外两个6位对应的是异常的“=”;
  // mod为1,证明还需补两个6位,一个是最后一个8位的后4位补两个0,另一个对应异常的“=”
  if (mod == 0) {
    result.push(base64hash.charAt((prev & 3) << 4));
    result.push('==');
  } elseif (mod == 1) {
    result.push(base64hash.charAt((prev & 0x0f) << 2));
    result.push('=');
  }
  return result.join('');
}

atob,btoa 不能编码解码中文

var str = "China,中国";
window.btoa(window.encodeURIComponent(str))
//"Q2hpbmElRUYlQkMlOEMlRTQlQjglQUQlRTUlOUIlQkQ="
window.decodeURIComponent(window.atob('Q2hpbmElRUYlQkMlOEMlRTQlQjglQUQlRTUlOUIlQkQ='))
//"China,中国"

编码含义

1,区分数据部分和参数部分
2,解决中文乱码(服务端和客户端编码不一致)

escape不编码字符有69个:*,+,-,.,/,@,_,0-9,a-z,A-Z
encodeURI不编码字符有82个:!,#,$,&,',(,),*,+,,,-,.,/,:,;,=,?,@,_,~,0-9,a-z,A-Z
encodeURIComponent不编码字符有71个:!, ',(,),*,-,.,_,~,0-9,a-z,A-Z

内存模型

JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量,堆存放复杂对象,池存放常量。

内存的生命周期

内存分配 — 内存使用 — 内存回收

内存回收算法

  • 引用计数算法

存在一个致命的问题:循环引用。如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。

  • 标记清除算法

标记清除算法将“不再使用的对象”定义为“无法达到的对象”。从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
内存泄漏

内存泄漏的识别方法

  • 浏览器方法
  • 命令行方法
  • WeakMap(ES6)

判断字节长度

破解循环引用

深拷贝的终极探索,破解循环引用(99%的人都不知道)

1
2
3
4
5
Uncaught TypeError: Converting circular structure to JSON
at JSON.stringify (<anonymous>)
at mainctrl.js:234
at Object.getUser (api.js:247)
at mainctrl.js:228

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Demo: Circular reference
var o = {};
o.o = o;

// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(o, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null;

JPG、GIF、PNG和BMP格式的图片各有什么优点和缺点

都说 WebP 厉害,究竟厉害在哪里?

1
2
3
4
5
BMP:优点(无损压缩,图质最好),缺点(文件太大,不利于网络传输)
GIF:优点(动画存储格式),缺点(最多256色,画质差)
PNG:优点(可保存透明背景的图片,压缩比高,生成文件体积小,16位48位颜色存储),缺点(画质中等)
JPG:优点(文件小,支持最高级别的压缩,利于网络传输),缺点(画质损失)
WebP 支持动画,小 ,浏览器支持不好

BMP

BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

GIF

GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式。其压缩率一般在50%左右,它不属于任何应用程序。目前几乎所有相关软件都支持它,公共领域有大量的软件在使用GIF图像文件。GIF图像文件的数据是经过压缩的,而且是采用了可变长度等压缩算法。GIF格式的另一个特点是其在一个GIF文件中可以存多幅彩色图像,如果把存于一个文件中的多幅图像数据逐幅读出并显示到屏幕上,就可构成一种最简单的动画。

PNG

PNG图像文件存储格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。

JPG

JPG全名是JPEG。JPEG图片以 24 位颜色存储单个位图。JPEG 是与平台无关的格式,支持最高级别的压缩,不过,这种压缩是有损耗的。渐近式 JPEG 文件支持交错。


URL.createObjectURL,FileReader上传图片预览

js图片前端预览之 filereader 和 window.URL.createObjectURL

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
URL.createObjectURL(blob)和FileReader.readAsDataURL(file)很相似:

区别

  • 通过FileReader.readAsDataURL(file)可以获取一段data:base64的字符串
  • 通过URL.createObjectURL(blob)可以获取当前文件的一个内存URL

执行时机

  • createObjectURL是同步执行(立即的)
  • FileReader.readAsDataURL是异步执行(过一段时间)

内存使用

  • createObjectURL返回一段带hash的url,并且一直存储在内存中,直到document触发了unload事件(例如:document close)或者执行revokeObjectURL来释放。
  • FileReader.readAsDataURL则返回包含很多字符的base64,并会比blob url消耗更多内存,但是在不用的时候会自动从内存中清除(通过垃圾回收机制)
  • 兼容性方面两个属性都兼容ie10以上的浏览器。

优劣对比:

  • 使用createObjectURL可以节省性能并更快速,只不过需要在不使用的情况下手动释放内存
  • 如果不太在意设备性能问题,并想获取图片的base64,则推荐使用FileReader.readAsDataURL


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
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title></title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>

<body>
<input type="file" name="" id="imgFile1" value="" />
<div class="preview_wrap1"></div>

<input type="file" name="" id="imgFile2" value="" />
<div class="preview_wrap2"></div>
</body>
<script type="text/javascript">
document.getElementById('imgFile1').onchange = function(e) {

var ele = document.getElementById('imgFile1').files[0];

var fr = new FileReader();
fr.onload = function(ele) {

var pvImg = new Image();
pvImg.src = ele.target.result;
pvImg.setAttribute('id', 'previewImg1');

$('.preview_wrap1').html('').append(pvImg);

}
fr.readAsDataURL(ele);
}
</script>

<script type="text/javascript">
document.getElementById('imgFile2').onchange = function(e) {
var ele = document.getElementById('imgFile2').files[0];

var createObjectURL = function(blob) {
return window[window.webkitURL ? 'webkitURL' : 'URL']['createObjectURL'](blob);
};
var newimgdata = createObjectURL(ele);

var pvImg = new Image();
pvImg.src = newimgdata;
pvImg.setAttribute('id', 'previewImg2');

$('.preview_wrap2').html('').append(pvImg);
}
</script>

</html>

字符串混淆处理

  • 加强密码等敏感数据传输安全性
  • 客户端加密,服务器端解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var softdog = require("softdog");

var input = {
type: "password",
value: "abc731"
}

// 客户端加密
var client2serverStr = softdog.encrypt(input.value);
console.log(client2serverStr);

// 服务器端解密
var rawStr = softdog.decrypt(client2serverStr);
console.log(rawStr);

// 结果校验
console.log(rawStr === input.value);
  • 特别提示

    加密|解密方法参数如果数据类型非字符串或者为空字符串,返回结果均为空字符串

Code

softdog
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
/***
* @module {Object} softdog 简单的字符串加密|解密,防止密码提交拦截
* @interface Softdog
* @method encrypt ---------- 字符串加密
* @method decrypt ---------- 字符串解密
*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
// 功能函数
function _confuse(str, sign) {
if (typeof str !== 'string' || !str) {
return '';
}
var newStr = '';
for (var i = 0; i < str.length; i++) {
newStr += String.fromCharCode(str.charCodeAt(i) + sign * 2);
}
return newStr;
}
;
var softdog = {
encrypt: function (str) {
return _confuse(str, 1);
},
decrypt: function (str) {
return _confuse(str, -1);
}
};
return softdog;
});

DEMO

判断是否为整数

Number.isInteger()

1
2
3
4
5
6
7
8
以前,没想过小数取余数会怎样,比如 JS 中 3.5%2 。
其结果为 1.5,也就是说可以用 v % 1 == 0 来判断 v 是否为整数了。

ES6
Number.isInteger()用来判断一个数值是否为整数。

Number.isInteger(25) // true
Number.isInteger(25.1) // false

css、js实现网页内容禁止选中

css

1
2
3
4
5
6
7
8
9
*{
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select:none;
-khtml-user-select:none;
-webkit-user-select:none;
-ms-user-select:none;
user-select:none;
}

通过body标签

1
2
<body oncontextmenu="return false;" onselectstart="return false">
//前面一句是禁止右键,后面一句是禁止复制。

通过js

1
2
3
//禁止页面选择以及鼠标右键
document.oncontextmenu=function(){return false;};
document.onselectstart=function(){return false;};