JavaScript笔记
# JavaScript笔记
# HTML标签
# 标签里添加空格
<!-- HTML -->
<!--  半个空白符 -->
<!--  一个空白符 -->
<h1>hello worid</h1>
2
3
4
# JS文档参考
# JS操作网页DOM节点
# JS动态/添加/移除/节点的Class
// 给节点添加className
document.querySelector('#div').classList.add('className')
// 移除节点添加className
document.querySelector('#div').classList.remove('className')
2
3
4
5
6
# JS动态创建节点
优点: 动态创建节点不会影响相邻的节点已经注册的事件
// 封装好的动态创建子节点函数
function createNode(nodeName, callback=null, parentNode=null){
let createNode = document.createElement(nodeName);
// 设置节点属性
const set = (attribute, value) => {
let att = document.createAttribute(attribute)
att.value = value;
createNode.setAttributeNode(att)
}
if(callback != null) callback(createNode,set)
if(parentNode != null) parentNode.appendChild(createNode);
}
// ------------------------------------------------------
// 使用示例
// 说明: 创建一个a节点不渲染生成进行点击事件
createNode('a',function(set,att){
a.href = '#hello'
set.click()
})
// 说明: 给body节点下添加一个div节点
// 获取父节点对象
let parentNode = node('body','g')[0]
// 创建一个div节点: 使用回调函数设置属性
createNode('div',function(set,att){
// set对象只能设置一些节点默认的属性、不能设置自定义属性
// 使用set给这个创建的div设置一个data属性是不生效的
// 因为div默认没有这个属性,只能通过att进行设置
set.className = 'myClicks'
set.id = 'ms'
set.innerHTML = '<a>hello</a>'
// att对象可以设置默认属性和自定义属性
// att('class','mac')
att('data','hello')
},parentNode)
// 创建后的样子
<body>
<div id='ms' class='myClicks' data='hello'>
<a>hello</a>
</div>
</body>
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
# JS运行平台
判断运行在什么平台的浏览器上
// 判断浏览器函数
function isMobile() {
if (window.navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) {
return true; // 移动端
} else {
return false; // PC端
}
}
2
3
4
5
6
7
8
# JS游览器本地存储
// 封装好的本地存储: 查看,存储,判断,删除,等等
// 本地数据存储操作等等
function storage(type, key=null, value=null){
// 判断一个值是否存在
const isKey = (key)=>{return localStorage.hasOwnProperty(key)}
// 获取存储所有的值
if (type == 'all') return localStorage.valueOf()
// 清除存储中所有的值
if (type == 'cll') {localStorage.clear(); return true }
// 判断没有输入key停止向下执行
if (key == null) {console.error('没有输入: Key'); return}
// 判断一个值是否存在
if (type == 'is') return isKey(key)
// 移除一个值
if (type == 'del') {localStorage.removeItem(key); if (isKey(key) == false) return true}
// 设置一个值
if (type == 'set' && value != null) {localStorage.setItem(key,JSON.stringify(value));return isKey(key)}
// 获取一个值
if (type == 'get' && key != null) return JSON.parse(localStorage.getItem(key))
}
// 使用示例:
// 查看本地存储所有数据
storage('all')
// 存储数据
storage('set','data','hello')
// 获取数据
storage('get','data')
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
# JS还原被重写的对象
例如调试一个第三方网站时:console对象下的方法被重写了、无法使用console.log进行输出、就需要还原成原来的方法、创建一个iframe标签并把里面的环境引入到当前对象下、实现还原
// 演示例如:
(function(){
var iframe = document.createElement('iframe')
document.body.appendChild(iframe)
window.console = iframe.contentWindow.console
}())
2
3
4
5
6
# JS函数或变量定义
# JS函数中的变量定义
// 声明多个变量的方法
// 声明多个变量或者初始化变量使用以下方法,简单实用
let i = 0,
index = 0,
array = [];
// es6通过数组形式声明或者初始化变量方法
let [i,index,array] = [0,0,[]]
2
3
4
5
6
7
8
9
10
11
# JS的new关键字构建对象和类class
# JS初始化对象new关键字
创建一个新对象;
将构造函数的作用域赋给新对象(因此
this
就指向了这个新对象;执行构造函数中的代码(为这个新对象添加属性;
返回新对象。
在《
JavaScript模式
》这本书中、当我们new
一个构造器,主要有三步:
- 创建一个空对象,将它的引用赋给
this
,继承函数的原型。 - 通过
this
将属性和方法添加至这个对象 - 最后返回
thi
指向的新对象,也就是实例(如果没有手动返回其他的对象)
# JS对象的 prototype/constructor/proto 说明
prototype
原型对象、constructor
指针对象、__proto
__指针对象 注意:constructor
对象、proto
对象、都是操作prototype
对象下的属性、方法的指针
# prototype原型对象
prototype原型
对象构成了js的原型链、通过prototype
对象来修改、设置对象下的属性和方法、注意使用: 使用prototype
对象,必须是在new
初始化之前设置、修改属性、方法
// 演示示例:
// 注意: [[Prototype]] == prototype
function Rect() {
this.name = '小明'
this.age = 18
this.speak = () => console.log(`我叫${this.name},今年${this.age}岁`)
}
// Rect对象继承数组Array对象下的属性、方法、继承后可以使用Array对象下的属性、方法
Rect.prototype = Array.prototype
// 给Rect对象自定义一个方法
Rect.prototype.privacy = function(){ console.log(`${this.name}喜欢看美女`)}
const rect = new Rect()
console.log(rect)
// 输出结果:
// Rect对象没有继承Array对象下的属性、方法时:
Rect {name: '小明', age: 18, speak: ƒ}
age: 18
name: "小明"
speak: ƒ ()
[[Prototype]]: Object
constructor: ƒ Rect()
[[Prototype]]: Object
// 输出结果:
// Rect对象继承Array对象下的属性、方法时:
// 明显多了许多属性、方法
Rect {name: '小明', age: 18, speak: ƒ}
age: 18
name: "小明"
speak: () => console.log(`我叫${this.name},今年${this.age}岁`)
[[Prototype]]: Array(0)
privacy: ƒ ()
at: ƒ at()
concat: ƒ concat()
constructor: ƒ Array()
copyWithin: ƒ copyWithin()
entries: ƒ entries()
every: ƒ every()
fill: ƒ fill()
filter: ƒ filter()
.....
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
# constructor指针对象
constructor
指针指向原来的构造函数、可以通过它解决new
初始化、调用对象后无法继承prototype
对象的属性、方法
// 演示示例:
// 通过constructor指针继承new初始化后prototype对象的属性、方法
function Rect() {
this.name = '小明'
this.age = 18
this.speak = () => console.log(`我叫${this.name},今年${this.age}岁`)
}
// 给Rect对象自定义一个方法
Rect.prototype.privacy = function(){ console.log(`${this.name}喜欢看美女`)}
const rect = new Rect()
console.log(rect)
// ------------------------------------------------>>
function Rect2(){}
// 通过prototype继承 Rect对象下的prototype下的属性,方法
Rect2.prototype = rect.constructor.prototype
const rect2 = new Rect2()
console.log(rect2)
// 输出结果:
Rect {}
[[Prototype]]: Object
privacy: ƒ ()
constructor: ƒ Rect()
[[Prototype]]: Object
// 输出结果:
Rect2 {}
[[Prototype]]: Object
privacy: ƒ ()
constructor: ƒ Rect()
[[Prototype]]: Object
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
# proto指针对象
// new初始化后使用__proto__对象来继承prototype对象继承原型下、属性、方法
function Rect() {
this.name = '小明'
this.age = 18
this.speak = () => console.log(`我叫${this.name},今年${this.age}岁`)
}
// Rect对象继承数组Array对象下的属性、方法、继承后可以使用Array对象下的属性、方法
// Rect.prototype = Array.prototype
const rect = new Rect()
// new初始化后无法使用prototype对象继承原型下、属性、方法
// 可以通过__proto__对象来继承prototype原型下、属性、方法
rect.__proto__ = Array.prototype
console.log(rect)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# prototype/class创建类对比
// 示例说明
// 从下面两个示例可以看出他们输出基本是一模一样
// 注意: class创建类优势更大
// 使用prototype原型创建类
function Rect() {
this.name = '小明'
this.age = 18
this.speak = () => console.log(`我叫${this.name},今年${this.age}岁`)
}
Rect.prototype.privacy = () => console.log(`${this.name}喜欢看美女`)
// ----------------------------------------------------------->>
// 使用class对象创建类
class Rect2{
constructor(){
this.name = '小明'
this.age = 18
this.speak = () => console.log(`我叫${this.name},今年${this.age}岁`)
}
privacy(){
console.log(`${this.name}喜欢看美女`)
}
}
const rect = new Rect()
console.log(rect)
const rect2 = new Rect2()
console.log(rect2)
// Rect输出结果:
Rect {name: '小明', age: 18, speak: ƒ}
age: 18
name: "小明"
speak: ƒ ()
[[Prototype]]: Object
privacy: ƒ ()
constructor: ƒ Rect()
[[Prototype]]: Object
// Rect2输出结果:
Rect2 {name: '小明', age: 18, speak: ƒ}
age: 18
name: "小明"
speak: ƒ ()
[[Prototype]]: Object
constructor: class Rect2
privacy: ƒ privacy()
[[Prototype]]: Object
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
# JS类定义
JS类定义或者使用 参考文档:JavaScript类(Class)完全指南 (opens new window)
# 创建类
// 定义类
class user {
// static # 定义静态私有方法:只能在类里面通过对象调用,外部无法通过对象调用
static #data = 0;
// new 初始化
constructor(name) {
this.name = name;
}
// static定义静态方法: 可以通过内部的类调用或者外部的类对象调用
// 例如:user.name、不能通过内部this和对象调用
static name(){
let name = "xiang ming";
console.log(name)
}
// 定义私有方法: 外部无法调用、内部使用this调用
#age(a){
return a + 1
}
// 定义方法: 外部后内部都能调用
main(){
let age = this.#age(18)
let name = this.name
console.log(name,age)
}
}
// new 时初始化类
let obj = new user('da ming')
// 调用对象方法
obj.main()
// 调用静态方法
user.name()
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
# 类继承
JavaScript
中的类使用extends
关键字支持单继承、在class Child extends Parent { }表达式中,Child类从Parent继承构造函数,字段和方法。
// 创建一个新的子类ContentWriter来继承父类User
// ContentWriter继承了User的构造函数,方法getName()和字段name。同样,ContentWriter类声明了一个新的字段posts。
// 注意,父类的私有成员不会被子类继承
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
}
const writer = new ContentWriter('John Smith');
writer.name; // => 'John Smith'
writer.getName(); // => 'John Smith'
writer.posts; // => []
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
# 子类继承父类属性方法
如果希望在子类中调用父构造函数,则需要使用子构造函数中可用的
super()
特殊函数。
// 让ContentWriter构造函数调用User的父构造函数,以及初始化posts字段
// 子类ContentWriter中的super(name)执行父类User的构造函数。
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
constructor(name, posts) {
super(name);
this.posts = posts;
}
}
const writer = new ContentWriter('前端小智', ['Why I like JS']);
writer.name; // => '前端小智'
writer.posts // => ['Why I like JS']
// 注意,在使用this关键字之前,必须在子构造函数中执行super()。调用super()确保父构造函数初始化实例。
class Child extends Parent {
constructor(value1, value2) {
//无法工作
this.prop2 = value2;
super(value1);
}
}
// 如果希望在子方法中访问父方法,可以使用特殊的快捷方式super。
// 子类ContentWriter的getName()直接从父类User访问方法super.getName(),这个特性称为方法重写。
// 注意,也可以在静态方法中使用super来访问父类的静态方法。
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
constructor(name, posts) {
super(name);
this.posts = posts;
}
getName() {
const name = super.getName();
if (name === '') {
return '无名氏';
}
return name;
}
}
const writer = new ContentWriter('前端小智', ['Why I like JS']);
writer.getName(); // => '无名氏'
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
# JS文件操作
使用
FileReader
对象读取本地文件:FileReader
对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用File
或Blob
对象指定要读取的文件或数据。注意:FileReader
对象读取的文件数据类型为Blob
对象的数据类型
# FileReader对象方法
// 作用:中止读取操作。
FileReader.abort()
// 作用:读取的文件数据转换为ArrayBuffer数据对象。
FileReader.readAsArrayBuffer()
// 作用:读取文件的原始二进制数据
FileReader.readAsBinaryString()
// 作用:读取文件的数据为URL格式的字符串以表示所读取文件的内容。此方法较为常用。
FileReader.readAsDataURL()
// 作用:读取文件文本内容
FileReader.readAsText()
2
3
4
5
6
7
8
9
10
# FileReader对象事件
// 作用: 该事件在读取操作被中断时触发。
FileReader.onabort = () => {}
// 作用: 该事件在读取操作发生错误时触发。
FileReader.onerror = () => {}
// 作用: 该事件在读取操作完成时触发。该事件较为常用。
FileReader.onload = () => {}
// 作用: 该事件在读取操作开始时触发。
FileReader.onloadstart = () => {}
// 作用: 该事件在读取操作结束时(要么成功,要么失败)触发。
FileReader.onloadend = () => {}
// 作用:该事件在读取Blob时触发
FileReader.onprogress = () => {}
2
3
4
5
6
7
8
9
10
11
12
13
# FileReader使用示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>本地文件读取</title>
</head>
<body>
<input id='readFile' type="file" />
<script type="text/javascript">
// 获取选中的本地文件
document.querySelector('#readFile').addEventListener('change', (e) => {
// 获取文件信息
const file = e.target.files[0];
if (file) {
// 初始化一个FileReader 对象
const reader = new FileReader();
// 读取文件数据为的ArrayBuffer数据对象
reader.readAsArrayBuffer(file);
// 文件读取成功时触发
reader.onloadend = function() {
// 这个事件在读取结束后,无论成功或者失败都会触发
if (reader.error) {
console.log(reader.error);
} else {
console.log(reader.result);
/*
输出结果:
ArrayBuffer(663)
byteLength: 663
[[Prototype]]: ArrayBuffer
[[Int8Array]]: Int8Array(663)
[[Uint8Array]]: Uint8Array(663)
[[ArrayBufferByteLength]]: 663
[[ArrayBufferData]]: 1
*/
}
}
}
})
</script>
</body>
</html>
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
# JS文本数据相互转换加解码
1.
FileReader
对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用File或Blob对象指定要读取的文件或数据。 2.Blob
对象表示一个不可变、原始数据的类文件对象、它的数据可以按文本或二进制的格式进行读取,也可以转换成ReadableStream来用于数据操作。Blob
表示的不一定是JavaScript
原生格式的数据、File
接口基于Blob
,继承了blob
的功能并将其扩展使其支持用户系统上的文件。 3.ArrayBuffer
对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写、视图的作用是以指定格式解读二进制数据。 4.Uint8Array
对象是ArrayBuffer
的一个数据类型(8位不带符号整数)。 5.TextEncoder
接受代码点流作为输入、并提供UTF-8
字节流作为输出。 6.TextDecoder
接口表示一个文本解码器、一个解码器只支持一种特定文本编码,例如utf-8、iso-8859-2、koi8、cp1261,gbk
等等。解码器将字节流作为输入,并提供代码点流作为输出。
// 演示示例:
// 示例1. TextEncoder -> ArrayBuffer
let encoder = new TextEncoder();
// 字符 转 Uint8Array
let uint8Array = encoder.encode("你好啊");
// Uint8Array 转 ArrayBuffer
let arrayBuffer = uint8Array.buffer
// 示例2. Blob -> ArrayBuffer
let str = 'hello,你好吗?'
let blob = new Blob([str],{type:'text/plain;charset=utf-8'});
let utf8decoder = new TextDecoder()
blob.arrayBuffer().then(buffer=>{
// ArrayBuffer
console.log(buffer)
let text = utf8decoder.decode(buffer)
// String
console.log(text)
})
// 示例3. FileReader => ArrayBuffer
let str = 'hello,你好吗?'
let blob = new Blob([str],{type:'text/plain;charset=utf-8'});
let utf8decoder = new TextDecoder()
let fr = new FileReader()
fr.readAsArrayBuffer(blob)
fr.onload = function(res) {
// ArrayBuffer
let buffer = fr.result
console.log(buffer)
let text = utf8decoder.decode(buffer)
// String
console.log(text)
}
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
# JS对象操作
# JS对象合并
利用了:
Object.assign
方法
// 演示例如:
let obj = {name:'xiangming'}
let obj2 = {num:12}
Object.assign(obj,obj2)
console.log(obj)
// 输出结果
{ name: 'xiangming', num: 12 }
2
3
4
5
6
7
8
# JS数据类型判断
使用
Object.prototype.toString.call
方法来进行数据类型判断比、typeof
、instanceof
、constructor
方法更加好 详细说明请参考: js判断数据类型 (opens new window)
// 演示示例:
var bool = true
var num = 1
var str = 'abc'
var und = undefined
var nul = null
var arr = [1,2,3]
var obj = {name:'haoxl',age:18}
var fun = function(){console.log('I am a function')}
// 定义数据类型判断函数
function type(data){
return Object.prototype.toString.call(data).replace('[object ','').replace(']','').toLowerCase()
}
// 输出
console.log(type(bool));
console.log(type(num));
console.log(type(str));
console.log(type(und));
console.log(type(nul));
console.log(type(arr));
console.log(type(obj));
console.log(type(fun));
// 输出结果:
boolean
number
string
undefined
null
array
object
function
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
# JS数组操作
# JS统计数组中元素重复次数
// 演示示例:
var arr = ["李","李","设","弟","弟","生","生","李"];
let obj = arr.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
console.log(obj);
// 输出结果
{李: 3, 设: 1, 弟: 2, 生: 2}
2
3
4
5
6
7
8
9
10
11
12
# JS获取数组第几到第几元素
// 演示示例: 获取数组前三个元素
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(0,3);
console.log("数组前三个元素:"+citrus);
// 输出结果
数组前三个元素:Banana,Orange,Lemon
2
3
4
5
6
7
# JS数组元素删除添加
// 演示示例
// 给数组尾部添加数
let arr = []
arr.push(2)
let arr = [1,2,3,4,5]
// 通过索引删除数组元素
let delete_index = 0
arr = arr.splice(delete_index, 1)
// 结果
[2,3,4,5]
// 通过指定元素删除数组元素
var arr = [1,2,3,4,5],
element = 2
// 删除数组中为2的数组元素
arr = arr.filter(item => item != element)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# JS数组分割
利用
array.slice()
方法来平均分割一个数组,并将分割后的数组组合成一个新的数组
// 定义演示数组
var arr = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5];
// 定义数组分割函数
function splitArray(arr, size) {
let i = 0,
index = 0,
array = [],
split = Math.ceil(arr.length / size, 10); // Math.ceil()向上取整的方法
while (i < split) {
array[i] = arr.slice(index, size + index);
index += size;
i++;
}
return array
}
// 将数组平均分割成4份
let arrs = splitArray(arr,4)
console.log(arrs)
// 输出结果:
[
[ 1, 1, 1, 1 ],
[ 1, 2, 2, 2 ],
[ 2, 2, 3, 3 ],
[ 3, 3, 3, 4 ],
[ 4, 4, 4, 4 ],
[ 5, 5, 5, 5 ]
]
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
# JS字符串操作
# JS字符串大小写转换
字符串方法 | 说明 |
---|---|
toLocaleLowerCase() | 把字符串转换成小写 |
toLocaleUpperCase() | 将字符串转换成大写 |
toLowerCase() | 将字符串转换成小写 |
toUpperCase() | 将字符串转换成大写 |
// 演示示例:
var s = "JavaScript";
console.log(s.toUpperCase()); //返回字符串“JAVASCRIPT”
2
3
# JS去除字符串首尾空格
let str = ' hello worid '
str.replace(/(^\s*)|(\s*$)/g, "")
// 输出结果
hello worid
2
3
4
5
# JS字符串查找匹配
// 示例
let str ='abcdefab';
// 静态匹配的字符
let arr = str.match(/a/g)
// 动态查找匹配的字符
str.match(RegExp(su,'g'))
console.log(arr)
// 输出结果
[ 'a', 'a' ]
// 匹配成功返回true
let su = 'ab'
let bool = RegExp(su,'g').test(str)
console.log(bool);
// 可以写成
bool = /d/g.test(str)
console.log(bool);
// 输出结果
true
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
# JS异步回调
# 使用异步回调说明
// 演示示例:
/**
下面示例说明:我需要获取setTimeout一秒后执行的结果、
console.log输出的是: 0,那是因为setTimeout是在console.log后异步执行的、我们没有办法在同步之上获取结果
**/
let num = 0
setTimeout(function(){
num++
},1000)
console.log(num)
// 输出: 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用Promise对象回调结果
Promise 被引入了用于解决著名的回调地狱问题,但是它们自身引入了复杂性以及语法复杂性。
// Promise对象使用示例:
let num = 0
const getData = function(){
return new Promise((resolve, reject) => {
// 模拟异步执行: 请替换你需要处理的异步
setTimeout(function(){
num++
if(num > 0){
// 获取正确结果回调
resolve(num)
}else{
// 获取错误结果回调
reject(num)
return
}
},1000)
})
}
// 调用方法
getData().then(res=>{
console.log('后异步',res)
{等待返回的结果后:在这里写需要处理的任务}
}).catch(err=>{
{等待返回的错误结果后:在这里写需要处理的任务}
console.log('后异步',err)
})
console.log('先同步',num)
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
# 使用Async/Await配合Promise实现回调
为什么引入
async/await
它们减少了promises的样板、且减少了promise链的不破坏链条
的限制
// 示例:封装async/await/Promise和方便调用
async function getAsyncAwait(callback) {
const request = () => {
return new Promise((resolve,reject) => {
// 模拟异步执行: 请替换你需要处理的异步
setTimeout(function(){
num++
if(num > 0){
resolve(num)
}else{
reject(num)
return
}
},1000)
})
}
// await等待异步获取返回的结果后回调
let resolve = await request()
callback(resolve)
}
// 调用方法
getAsyncAwait((res,err)=>{
console.log(res)
{等待返回的结果后:在这里写需要处理的任务}
})
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
# 类里面使用async/await/Promise参考
# URL编码
# URL编码和解码
// 只对URL的参数进行编码或者解码
encodeURI(URL) // 编码
decodeURI(URL) // 解码
// 只对URL整个连接进行编码或者解码
encodeURIComponent(URL) // 编码
decodeURIComponent(URL) // 解码
// 演示示例:
encodeURI('http://iecoxe.top:5000/v1/kuwo/search?key=你好')
// 输出结果: 'http://iecoxe.top:5000/v1/kuwo/search?key=%E4%BD%A0%E5%A5%BD'
encodeURIComponent('http://iecoxe.top:5000/v1/kuwo/search?key=你好')
// 输出结果: 'http%3A%2F%2Fiecoxe.top%3A5000%2Fv1%2Fkuwo%2Fsearch%3Fkey%3D%E4%BD%A0%E5%A5%BD'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# HTTP请求
参考: 文档:XMLHttpRequest (opens new window) 文档:同步和异步请求 (opens new window)
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.open(method, url, async);
xhr.open(method, url, async, user);
xhr.open(method, url, async, user, password);
// 参数
method: 要使用的 HTTP 方法,比如 GET、POST、PUT、DELETE、等。对于非 HTTP(S) URL 被忽略。
url: 一个 DOMString 表示要向其发送请求的 URL。
async: 可选的布尔参数,表示是否异步执行操作,默认为 true。如果值为 false,send() 方法直到收到答复前不会返回。如果 true,已完成事务的通知可供事件监听器使用。如果 multipart 属性为 true 则这个必须为 true,否则将引发异常。
user: 可选的用户名用于认证用途;默认为 null。
password: 可选的密码用于认证用途,默认为 null。
2
3
4
5
6
7
8
9
10
11
12
13
# HTTP性能
# HTTP1.1
HTTP1.1协议
请求只能串行发送:HTTP1.1协议
规定请求只能串行发送
,也就是说一百个请求必须依次逐个发送,前面的一个请求完成才能开始下个请求 虽然不会花费tcp三次握手时间:但是本身是串行请求HTTP1.1
如果要同时发起多个请求,就得建立多个 TCP 连接,因为一个 TCP 连接同时只能处理一个HTTP1.1
的请求。
# HTTP2
在
HTTP2
上,多个请求可以共用一个 TCP 连接,这称为多路复用。同一个请求和响应用一个流来表示,并有唯一的流 ID 来标识。 多个请求和响应在 TCP 连接中可以乱序发送,到达目的地后再通过流 ID 重新组建。
# 异步请求
演示一个简单的异步请求
// 第 2 行中指定第三个参数为 true,表示该请求应该以异步模式执行。
// 第 3 行创建一个事件处理函数对象,并将其分配给请求的 onload 属性。此处理程序查看请求的 readyState,以查看事务是否在第 4 行完成,如果是,并且 HTTP 状态为 200,则转储接收到的内容。如果发生错误,则显示错误消息。
// 第 15 行实际上启动了请求。只要请求的状态发生变化,就会调用回调程序。
var xhr = new XMLHttpRequest();
xhr.open("GET", "/bar/foo.txt", true);
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.send(null);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 设置请求超时
示例: 设置请求超时
// 你可以使用一个超时设置,来避免你的代码为了等候读取请求的返回数据长时间执行。超时毫秒数可以通过为 XMLHttpRequest 对象的 timeout 属性赋值来指定
var xhr = new XMLHttpRequest();
xhr.ontimeout = function () {
console.error("The request for " + url + " timed out.");
};
xhr.onload = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.open("GET", url, true);
xhr.timeout = 2000;
xhr.send(null);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 同步请求
例子:
http
同步请求
var request = new XMLHttpRequest();
request.open('GET', 'http://www.mozilla.org/', false);
request.send(null);
if (request.status === 200) {
console.log(request.responseText);
}
2
3
4
5
6
# http请求封装
// http请求
class httpRequest{
// new 初始化时执行
constructor(timeout = 4000){
this.headerData = null
this.requestHeader = []
this.timeout = timeout
}
// 定义私有方法: 格式化请求头
#setHeader(xhr,header){
// xhr无法设置的请求头
let disableHeader = `
Accept-Charset Accept-Encoding Access-Control-Request-Headers
Access-Control-Request-Method Connection Content-Length
Cookie Cookie2 Date DNT Expect Host Keep-Alive Origin User-Agent
Proxy- Sec- Referer TE Trailer Transfer-Encoding Upgrade Via
`;
let data = []
if(this.headerData != header){
this.headerData = header;
header = header.split('\n')
header.forEach(list =>{
list = list.replace(/(^\s*)|(\s*$)/g, "")
if(list.length > 0){
list = list.split(':')
if(list.length > 1){
let bool = RegExp(list[0]).test(disableHeader),
bool2 = RegExp(list[0].split('-')[0]).test(disableHeader)
if(bool||bool2){
console.log('无法设置请求头: ',list[0])
}else{
xhr.setRequestHeader(list[0],list[1])
data.push(list)
}
}
}
})
this.requestHeader = data;
}else{
this.requestHeader.forEach(list=>{
xhr.setRequestHeader(list[0],list[1])
})
}
}
// http发送数据
send(url,header=`null`,data){
let xhr = new XMLHttpRequest();
xhr.open("POST",url,true);
if(header != null) this.#setHeader(xhr,header)
xhr.send(data);
}
// http同步请求:效率低:容易操作
getSync(url, header=`null`, statusCode=200) {
let res = false;
let xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
if(header != null) this.#setHeader(xhr,header)
xhr.send();
if (xhr.status === statusCode) {
res = JSON.parse(xhr.responseText)
} else {
console.error(xhr.statusText);
}
return res;
}
// http异步等待实现:同步:效率中:不好操作
async getAsyncAwait(url, callback, header=`null`, statusCode=200){
let _this = this,
asyncFuns = this.getAsync
const request = () => {
return new Promise(resolve => {
asyncFuns(url,resolve,header,statusCode,_this)
})
}
let resolve = await request()
callback(resolve)
}
// http异步请求:效率高:不好操作
getAsync(url, callback, header=`null`, statusCode=200, _this=null){
if(_this == null) _this = this
let timeout = _this.timeout
let xhr = new XMLHttpRequest();
if(timeout != 0) xhr.ontimeout = ()=>{ console.error('请求超时了: ' + url) }
xhr.onload = function() {
if (xhr.readyState === 4 && xhr.status === statusCode) {
callback(JSON.parse(xhr.responseText));
} else {
console.log('错误',xhr.statusText);
}
}
xhr.open("GET", url, true);
if(header != null) _this.#setHeader(xhr,header)
if(timeout != 0) xhr.timeout = timeout;
try{
xhr.send();
}catch(e){
console.log('请求失败')
xhr.send();
}
}
}
// 请求头格式
let header = `
Accept: */*
scheme: https
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alives
Host: www.kuwo.cn
Referer: https://www.baidu.com/
Sec-ch-ua-platform: macOS
Set-Cookie: widget_session=abc1235; SameSite=None; Secure
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36
`
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
# JS性能优化
# 避免if嵌套造成执行效率低下问题
// 例如:这样
if
if
if
if
do something
endif
endif
endif
endif
2
3
4
5
6
7
8
9
10