ES6学习笔记 📑
babel工具本地使用
在线:
下载到本地(下载到项目里):npm init -y(初始化,且不会出现询问,之后生成.json文件)npm install @babel/core(安装babel核心––抽象语法树)npm install @babel/preset-env(所有插件)npm install @babel/cli(脚手架)
编写.babelrc文件(json写法):
ES6转换为ES5的方法
后面加––watch就可以实时转换
变量声明新形式之let
- let 不会声明提升不可以重复声明同一变量
- let a=10;时window.a里没有 var a=10;时window.a ==10
- let有具有块级作用域{ 这里的let只能在这里用 }
- 暂时性死区:会先扫描作用域内的变量,但不声明提升,如果先用变量再声明,就会报错
- 可以解决for循环的闭包问题
变量声明新形式之const
- const声明的是常量 const a = 10 a里的值不能被改变; 如果 const a; a = 10;会报错;
- 在编写时先用const 如果有必要才用let、
- 存储常量的空间里的值不能发生改变 例如上面1里的例子;但是const p = { }; p.a = 10;这时对象里的值是可以改变的,因为p空间里存的是对象的地址,只要不给p重新赋值就行
spreed&rest即 ...(ES6)
- ...展开和收集运算符:此运算符在不同地方使用有不同的功能,可以从写和度两个角度分析。写:收集作用,收集到数组里
function test(...arg){};test(1,2,3};
1
读:输出:1,2,3 展开作用 展成散列
var arg = [1,2,3]; console.log(...arg);
1
- 写:函数传参时,前面可以写参数,最后写扩展运算符的参数,扩展运算符的参数后不能再写参数,即扩展运算符的参数只能作为最后一个参数;
function test(a, b, ...arg){};test(1,2,3};
1
spreed&rest(ES7)
- 下面的例子是浅克隆
- 下面例子模仿深克隆,但是如果被克隆对象层级过多,编写会特别复杂
- 数组的深克隆方法
- 深克隆最好还是原生JavaScript写的克隆函数
- ES6新的浅克隆方法
destructuring解构
- 对象的解构
const obj = { name:'lxl', age: '18' };
const {name: nameO, age: ageO} = obj;
1
2
2
- 默认赋值:
const {name: nameO, age: ageO, sex = 'male'} = obj;
1
- 解构数组:
可以 const arr = [1, 2, 3]; const [x, y, z] = arr
还可以 const arr = [1, 2, 3]; const {0: x, 1: y, 2: z} = arr
1
2
2
- 解构赋值时,某些参数不要可以不写,但结构不能省略
const arr = [1, 2, 3, {name: 'lxl'}]; const [,,,{name}] = arr;
1
- 解构赋值主要是解决复杂数据操作时的简化。
箭头函数
- 箭头函数的特点:不用写function关键字;只能作为函数使用不能new,没有原型;参数不能重复命名;返回值可以不写return,但是有时需要配合{};内部arguments this由定义时外围最接近一层的非箭头函数的arguments this决定其值。
- 声明箭头函数必须要有变量、对象或数组等去存箭头函数。
对象不可以用上面的方法,应该在对象外面加上()变成表达式:
- 简写嵌套函数:箭头函数只有一个参数时,参数外面的括号可以省略; 箭头函数里形参不能相同(a, a)是错的。
上面是箭头函数简写的,原理如下
- arguments的特点
- this的说明
ES5之Object.defineProperty
Object.defineProperty概念
有value 和 writable就不能有set()和get(),有set()和get()就不能有value 和 writable
数据劫持
- 原生js数据劫持写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="text" id='demo'>
<div id="show"></div>
<script>
//针对对象的数据监测
var oDiv = document.getElementById('show');//获取div
var oInput = document.getElementById('demo');//获取input
var oData = {
valueObj:{
value: 'duyi',
},
};
oInput.oninput = function () {//通过oninput事件监测input里的值是否有变化
oData.value = this.value;//如果有变化就把值赋给数据池oData
};
function upDate () {
oDiv.innerText = oData.valueObj.value;//将数据赋值给div
}
// upDate ();
//监测 数据池数据oData的某个属性是否有变化
function Observer (data) {
if (!data || typeof data != 'object') {//如果没传来数据对象或者传来的是undefined就直接返回出去
return data;
};
object.keys(data).forEach(function (key) {//遍历data里所有属性
definedRective(data, key, data[key]);
});
};
//监测方法
function definedRective (data, key, val) {
//利用递归的方法判断val是不是对象
Observer(val);
//更改对象属性时,就会触发get和set方法,就能实现监测属性值的目的
object.defineProperty(data,key, {
get () {//读时触发
return val;//这里形成了闭包
},
set (newVal) {//写时触发
if(newVal == val) return;//如果传进来的值和原来值一样就不需要改变
val == newVal;//将改变值赋值给数据池val
upDate ();
},
})
};
Observer(oData);
//针对数组的数据监测
let arr = [];
let {push} = Array.prototype;
function upData () {
console.log('更新');
};
object.defineProperty(Array.prototype, 'push', {
value: (function () {
return (...arg) => {
push.apply(arr, arg);
upData();
}
})()
});
arr.push(1,2);
</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
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
- 针对对象、数组的数据变化进行监测,并赋值给页面
ES6之Proxy和Reflect
- Proxy和Reflect暂时兼容性较差,使用的地方很少
- 对要访问的对象、数组的方法进行改写,
- 比数据劫持的写法要简化很多,下面的没有对多层对象进行操作,还应该写递归 图片
ES6之class
- 相当与ES5里的构造函数,但解决了原来构造函数的继承等问题。本质还是函数。
- class用法
class Plane {//定义class类
//ES6上只能定义静态方法,不能定义静态属性 static alive = 11;是错的 但在ES7上是可以的
static alive () {//定义一个静态方法,这个是plane的方法,通过Plane.alive()进行调用,不是原型上的方法
return true;
}
constructor (name) {//定义私有属性
this.name = name || '普通飞机';
this.blood = 100;
}
fly () {//定义原型上的方法
console.log('fly');
}
//ES6上不能在原型上加属性:nb = 10;是错的 但在ES7上是可以这样写,但是是私有属性
};
class AttackPlane extends Plane {//继承Plane原型属性
//静态属性继承后,也是继承到AttackPlane上的
constructor(name) {//传参
super(name);//只有第一行写这个,才能继承私有属性,可以传参
this.logo = 'bob';
}
dan () {
console.log('biubiu');
}
}
var oPa = new AttackPlane('攻击');
// var oP = new Plane();//创建一个新对象,必须new才能用
// oP.fly();
//class出来的类的原型,不能被枚举
//
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
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
- 不存在声明提升;有取值函数get()和存值函数set()方法:
- class的继承,注意事项用到再看
ES7之class提案属性-----装饰器
- 对类、类里的私有属性、原型属性进行装饰,改变其原有的功能
- 现阶段主要是用于框架开发用
// 张三
let oInput = document.getElementById('inp');
let oBtn = document.getElementById('btn');
@Skin//修饰一个类
class Search {
constructor () {
this.keyValue = '';
}
@myReadOnly
url = 'urlA-';//要装饰的私有属性上一行,写@+名字,在类外面写函数对属性进行装饰
@dealData('张三')//可以传参
getContent (a, b) {
console.log('向' + this.url + '发送网络请求, 数据:' + this.keyValue, a, b);
return 10;
}
};
装饰类
function Skin (target) {//target是类本身
target.aaa = 30;//给类添加一个静态属性
}
let oS = new Search();
// oS.url = 20;
oInput.oninput = function () {
oS.keyValue = this.value;
};
oBtn.onclick = function () {
oS.getContent(1, 2);
};
// 李四
装饰私有属性
function myReadOnly (proto, key, descriptor) {//PROTO是原型,key是要装饰的私有属性名,descriptor是装饰方法的对象,
// console.log(proto, key, descriptor);
descriptor.writable = false;
descriptor.initializer = function () {//initializer是descriptor上的方法,可以取到属性值,并通过赋值函数进行操作
return 6;
}
}
原型上的方法的话
//proto是要修饰的对象, key要修饰的属性名, descripto该属性的描述对象
function dealData (proto, key, descriptor) {//装饰原型上的属性时,没有initializer,有value
console.log(proto, key, descriptor);
let oldValue = descriptor.value;//descriptor.value就是要装饰的方法getContent (a, b) {
// 代理思想
descriptor.value = function () {
var urlB = 'urlB-';
console.log('向' + urlB + '发送网络请求, 数据:' + this.keyValue);//处理一个新功能
return oldValue.apply(this, arguments);//原来的功能继续执行
}
}
function dealData (ms) {//传参
return function (proto, key, descriptor) {
console.log(proto, key, descriptor);
let oldValue = descriptor.value;
// 代理思想
descriptor.value = function () {
var urlB = 'urlB-';
console.log('向' + urlB + '发送网络请求, 数据:' + this.keyValue + ms);
return oldValue.apply(this, arguments);
}
}
}
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
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
ES6之set
- set和map的兼容性不好,应用场景很少,不怎么用
- set是构造函数,用来存储数据
- set上的方法 os.add(数据)添加数据; os.delete(数据)删除数据,要删除数组,数组先拿变量接收;os.clear()清空;os.has(数据)是否有这个数据;
let oS = new Set(['a', 'b', 'c', ['a', 1, 2], {name: 'cst'}]);//定义一个set,并传入数据
let oS2 = new Set('abc');//输出"a" "b" "c"
oS2.add(9);//往os2里存入数据
// api
let oS3 = new Set();
oS3.add(true);
oS3.add(1);
delete//删除某数据
clear//清空数据
has//存在某数据
具备Iterator接口的值 arr set map string
for (let val of oS2) {//利用for of遍历set,利用for of 变量的必须具备迭代接口
console.log(val);
};
oS2.forEach(val => {//遍历set
});
Set与arr之间的转换
//转换为数组更容易操作
let arr = [1, 2, 4, 3, 2, 1, 3, 4, 5, 1];
let oS = new Set(arr);//将数组转换为set
// 两种方式可以将set转换为数组
console.log([...oS]);//需要有迭代接口
Array.from(oS)//将类数组、具有迭代器的转换为数组
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
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
- set的应用 应用一
//应用一:数组去重
let o2 = {
name: 'cst'
}
let arr = [1, 2, 4, 3, 2, 1, 3, 4, 5, 1, o2, o2, {name: 'cg'}];
// Set
let oS = new Set(arr);
let o = {
name: 'cst'
}
let arr = [1, 2, 3, 4, 5, o, 6, 1, o, 2, 3, {name: 'cg'}];
let obj = {};
let newArr = [];
//利用for循环进行去重时,对数组里的对象不能去重,数组里所有的对象会只留下一个
//对象在数组里会调用tostring方法,之后输出[object Object],因此只会留下一个
for (let i = 0; i < arr.length; i++) {
if ( !obj[ arr[i] ]) {
newArr.push(arr[i]);
obj[arr[i]] = true;
}
};
console.log( newArr );
//利用Set进行数组去重
let o = {
name: 'cst'
}
let arr = [1, 2, 3, 4, 5, o, 6, 1, o, 2, 3, {name: 'cg'}];
let oS = new Set(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
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
应用二
//应用二:并集 交集和差集(不在前端搞,后台)
//并集:两个数组合并并去重
let arr1 = [1, 2, 3, 2, 3];
let arr2 = [3, 2, 4, 4, 5];
Set
let oS = new Set([...arr1, ...arr2]);//合并去重
//交集:两个数组共有的部分并去重
let arr1 = [1, 2, 3, 2, 3];
let arr2 = [3, 2, 4, 4, 5];
let oS1 = new Set(arr1);//去重
let oS2 = new Set(arr2);//去重
let newArr = [...oS1].filter( ele => oS2.has(ele) );//如果oS2里有ele就返回给一个新数组
console.log(newArr);
//差集:两个数组不共有的部分并去重
[1, 4, 5];
let arr1 = [1, 2, 3, 2, 3];
let arr2 = [3, 2, 4, 4, 5];
let oS1 = new Set(arr1);//去重
let oS2 = new Set(arr2);//去重
let newArr1 = [...oS1].filter(ele =>!oS2.has(ele));//如果oS2里没有ele就返回给一个新数组
let newArr2 = [...oS2].filter(ele =>!oS1.has(ele));//如果oS1里没有ele就返回给一个新数组
console.log( [...newArr1, ...newArr2] );//将两个新数组合并
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
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
ES6之map
- 基本用法:
初始化
let oMp = new Map([['name', 'cst'], ['age', 18], ['sex', true], [{}, '----']]);//map的参数是一个数组,数组里每个元素都要放到数组里,每个元素以键值对形式存在
console.log(oMp);//输出跟对象类似,但不是对象,是一种新的存储形式
api
let oMp = new Map();
oMp.set('name', 'cst');//用set往map里存值,以键值对的形式
oMp.set('age', 18);
let obj = {};
oMp.set(obj, '----');
oMp.set({}, '+++++');
oMp.get('name');//取值
oMp.get({});//取引用值是取不到的,根本就不是一个对象
oMp.get(obj);//取对象就要把对象存在变量里,再取值
Map.prototype//可以查看原型上有哪些方法
//有delete、clear、size(长度)、keys(所有属性名取出来)、entries(取键值对)、has(有哪些属性)
forEach//遍历map
oMp.forEach((ele, key, self) => {//ele:属性值 key:属性名 self:它自己
console.log(ele, key, self);
});
for (let val of oMp) {//for of遍历处理的是键值对形式的数组
console.log(val[0], val[1]);
};
for (let val of oMp.keys()) {
console.log(val);//输出所有属性名
};
for (let val of oMp.entries()) {//entries有点鸡肋
console.log(val);//输出所有键值对
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
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
- map原理
// Map 实现原理
//链表:举例{} {} {} {} {}将几个对象联系起来
let node3 = {
key: 'name3',
value: '3',
next: null
};
let node2 = {
key: 'name2',
value: '2',
next: node3//记录另一个对象
};
let node1 = {
key: 'name',
value: '1',
next: node2//记录另一个对象 通过这个属性进行链接
};
//通过node1.next找到node2,通过node1.next.next找到node3,
name name//map存两个相同属性名,后一个会覆盖前一个
let oMp = new Map([['name1', '1'], ['name2', '2']]);//每个元素是以对象的形式存储
//每个对象会有很多属性,其中一个属性是用来链接各属性的
//在取值的时候,通过调用属性名,根据hash算法,得到属性值
hash ->
{} 'name1' 'age' 1 true NaN//map键值可能不一样 不定的值存在一个特殊的范围如0 - 8
//通过hash算法 弄出 特点范围的值
//桶 可以利用数组做一个桶,桶里有八个位置
[
{
next: {
key: 'name1',
value: '1',
next: {
key: 'name2',
value: '2'
}
}
},
{},
{},
{},
{},
{},
{},
{}
];
//map具体实现
let prop = 'name';
let obj = {
[prop + 10]: 'cst' //ES6可以将变量放在[]里作为属性名
}
function myMap () {
this.bucketLength = 8;
this.init();
}
myMap.prototype.init = function () {
// 初始化
this.bucket = new Array( this.bucketLength );//桶的位置有八个
for (var i = 0; i < this.bucket.length; i++) {
this.bucket[i] = {
type: 'bucket_' + i,//遍历每个桶,起个编号
next: null//初始化时,让每个桶是空的
}
}
}
//写hash算法
// 1. [0, 8)
// 2. 重复算值固定
myMap.prototype.makeHash = function (key) {
let hash = 0;
// string
if (typeof key !== 'string') {
if (typeof key == 'number') {
//number NaN
//Object.is(key, NaN)是ES6里的新方法,用来判断里面的两个参数是否相等
hash = Object.is(key, NaN) ? 0 : key;//如果key等于NaN就赋0,否则赋key
}else if (typeof key == 'object') {
// null {} []
hash = 1;
}else if (typeof key == 'boolean') {
// true false boolean
hash = Number(key);
}else {
// undefined function(){}
hash = 2;
}
}else {
// string
// 'a' 'ab' 'asdasdadasda';
// 如果长度大于等于3 ,取前三个字符,将ascii 累加
for (let i = 0; i < 3; i++) {
// key[i]取出字符串中的某一位,charCodeAt(0)取ascii值
hash += key[i] ? key[i].charCodeAt(0) : 0;//如果有值就取ascii值,没有就累加0
}
}
return hash % 8;//让hash在0-8之间
}
myMap.prototype.set = function (key, value) {
let hash = this.makeHash(key);//算key的hash值
let oTempBucket = this.bucket[hash];//桶中的位置
while (oTempBucket.next) {//如果oTempBucket.next存在执行下面
if (oTempBucket.next.key == key) {//如果有key就覆盖value
oTempBucket.next.value = value;
return;
}else {//如果没有key,把oTempBucket.next赋给下一个,就是在做链式
oTempBucket = oTempBucket.next;
}
};
oTempBucket.next = {//如果oTempBucket.next不存在执行下面
key: key,
value: value,
next: null
};
}
myMap.prototype.get = function (key) {
let hash = this.makeHash(key);//算key的hash值
let oTempBucket = this.bucket[hash];//桶中的位置
while(oTempBucket) {
if (oTempBucket.key == key) {//如果有key就返回value
return oTempBucket.value;
}else {//如果没有key,把oTempBucket.next赋给下一个,就是往下找
oTempBucket = oTempBucket.next;
}
}
return undefined;//如果没找到就undefined
}
myMap.prototype.delete = function (key) {
let hash = this.makeHash(key);
let oTempBucket = this.bucket[hash];
while (oTempBucket.next) {
if (oTempBucket.next.key == key) {
oTempBucket.next = oTempBucket.next.next;//next赋值给前一个
return true;
}else {
oTempBucket = oTempBucket.next;
}
}
return false;
}
myMap.prototype.has = function (key) {
let hash = this.makeHash(key);
let oTempBucket = this.bucket[hash];
while (oTempBucket) {
if (oTempBucket.next && oTempBucket.next.key == key) {
return true;
}else {
oTempBucket = oTempBucket.next;
}
}
return false;
};
myMap.prototype.clear = function (key) {
this.init();
};
//
let oMp = new myMap();
let obj1 = {
name: 'cst'
}
oMp.set('name1', 'cst1');
oMp.set('name2', 'cst2');
oMp.set(obj1, '---');
oMp.set(obj1, '+++');
oMp.set(function () {}, 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
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
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
ES6之promise使用
- promise是内置的构造函数
- Promise new执行
let oP = new Promise();
// Promise执行时,要执行executor 函数,同步执行
let oP = new Promise(() => {
//这里要同步执行
console.log('0');
});
console.log('1');//先输出 0 再输出 1
1
2
3
4
5
6
7
2
3
4
5
6
7
- Promise 承诺异步操作 // 可以对比学习Deferred(jQuery里的异步操作)---> 三种状态(done fail progress) 触发三种状态的方法(done fail progress)
setTimeout(() => {
console.log('setTime');
}, 0);
let oP = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
Math.random() * 100 > 60 ? resolve('ok') : reject('no');
}, 1000);
console.log(0);
resolve(1);//被触发后会异步执行
});
console.log(1);
//只有注册then才能 触发Promise回调
// then 只能注册两个回调函数,一个成功,一个失败
// 异步执行
// 宏任务:setTimeout() Ajax回调
// 微任务: then
// 宏任务——>优先放到队列里 微任务 ——>后放到队列里 (宏任务与微任务不在一个队列里执行)
// task queue 1 task queue 2
// 微任务有优先 执行权
// jq 你不知道的js UI 多线程
oP.then((val) => {
console.log(val);//成功状态时,执行resolve('ok')回调,就是执行 console.log(val)
}, (reason) => {
console.log(reason);//失败状态时,执行reject('no'),就是执行console.log(reason)
});
console.log(2);
//输出 0 2 1 setTime
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
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
- 链式操作
let oP = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
Math.random() * 100 > 60 ? resolve('ok') : reject('no');
}, 1000);
resolve('ok');
});
// 链式操作.then 后再 .then
// 上一个then不抛出错误的话,那下一个then只执行成功的函数
oP.then((val) => {
console.log(val);
throw new Error('duyi');// 上一个then 注册的函数抛出错误 下一个then的失败回调就会执行
// 返回值 Promise 对象,将触发下一个.then的成功或失败的回调
// return new Promise((resolve, reject) => {
// reject('newPromise ok');
// });
}, (reason) => {
console.log(reason);
return 30;// 上一个then返回值作为下一个then注册函数的执行参数
}).then((val) => {
console.log('ok then2:' + val);
}, (reason) => {
console.log('no then2:' + reason);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- node里的回调练习
let fs = require('fs');
function readFile (path) {
return new Promise((res, rej) => {
fs.readFile(path, 'utf-8', (err, data) => {//读取一个文件
if (data) {
res(data);//如果有数据就可以被触发执行
}
});
});
}
readFile('./data/number.txt').then((data) => {//别忘了readFile传参
return readFile(data);//成功状态执行,读取文件
}).then((data) => {
return readFile(data);//成功状态执行,读取文件
}).then((data) => {
console.log(data);//成功状态执行,打印文件
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 捕获异常
let oP = new Promise((res, rej) => {
// throw new Error('duyi');
res();
});
// 链式调用的时候如果写一个空then其实相当于不存在可以忽视
oP.then(() => {
throw new Error('duyi');//这里抛出错误,只要后面的.then里有触发错误的函数就可以处理
}, (reason) => {
console.log(reason)
}).then().then(() => {
}).then(null, (reason) => {
console.log(reason)
}).catch((err) => {//前面有能捕获异常的这里的catch就不能用了,如果没有就捕获
console.log(98, err);
return 10;//返回值不能被下面.then调用
}).then((val) => {//上面有catch下面.then也可以用,catch如果抛出错误,就触发错误函数
console.log('catch after ok:' + val)
}, (reason) => {
console.log('catch after no:' + reason)
}).finally(() => {//最后的回调,后面不能再接.then了
console.log('over')
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- .all后面的()里只能写数组,.all里回调全部成功触发.then成功,.all里回调一个失败触发.then失败
Promise.all([readFile('./data/number.txt'), readFile('./data/name.txt'), readFile('./data/score.txt')]).then((val) => {
console.log(val);//val是所有回调的执行结果
});
1
2
3
2
3
// Promise.all可以将多个Promise实例包装成一个新的Promise实例。 // 同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组, // 而失败的时候则返回最先被reject失败状态的值。
function test (x) {
return new Promise((res, rej) => {
setTimeout(() => {
Math.random() * 100 > 50 ? res(x) : rej(x);
}, 100)
});
}
let oP = Promise.all([test('a'), test('b'), test('c')]);
oP.then((val) => {
console.log(val)
}, (reason) => {
console.log(reason);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- race 赛跑 .race里回调最先完成的触发.then成功或失败
Promise.race([readFile('./data/number.txt'), readFile('./data/name.txt'), readFile('./data/score.txt')]).then((val) => {
console.log(val);
});
1
2
3
2
3
mise.race就是赛跑的意思,意思就是说, // Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果, // 不管结果本身是成功状态还是失败状态。
ES6之promise----用ES5模拟实现
function MyPromise (executor) {
var self = this;//让this指向自己,防止指向window
self.status = 'pending';//正在进行的状态码
self.resolveValue = null;//为了让.then方法能操作resolve 和reject的参数,设置一个值被调用
self.rejectReason = null;
self.ResolveCallBackList = [];
self.RejectCallBackList = [];
function resolve (value) {
if (self.status === 'pending') {
self.resolveValue = value;//将value参数传给self.resolveValue,便于.then操作
self.ResolveCallBackList.forEach(function (ele) {//要异步执行的时候再执行
ele();
});
}
}
function reject (reason) {
if (self.status === 'pending') {
self.status = 'Rejected';//更改为失败的状态码
self.rejectReason = reason;//将reason参数传给self.rejectReason,便于.then操作
self.RejectCallBackList.forEach(function (ele) {
ele();
});
}
}
try {
executor(resolve, reject);
}catch(e) {
reject(e);//如果 executor(resolve, reject);抛出错误,就执行reject(e)
}
};
function ResolutionRetrunPromise (nextPromise, returnValue, res, rej) {//.then 返回Promise后,要触发下一个.then
if (returnValue instanceof MyPromise) {
// Promise 对象
returnValue.then(function (val) {
res(val);
}, function (reason) {
rej(reason)
});
}else {
res(returnValue);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
//空then处理
if (!onFulfilled) {//.then没有传成功触发函数时,执行
onFulfilled = function (val) {
return val;
}
}
if (!onRejected) {//.then没有传失败触发函数时,执行
onRejected = function (reason) {
throw new Error(reason);
}
}
var self = this;
var nextPromise = new MyPromise(function (res, rej) {//创建一个新的MyPromise,为.then能实现链式操作
if (self.status === 'Fulfilled') {
setTimeout(function () {//没有微任务的权限,setTimeout模拟异步执行
try {//如果出现错误就抛出错误
// var nextResolveValue = onFulfilled(self.resolveValue);
// res(nextResolveValue);
var nextResolveValue = onFulfilled(self.resolveValue);
ResolutionRetrunPromise(nextPromise, nextResolveValue, res, rej);//返回Promise后处理
}catch(e) {
rej(e);//抛出错误就执行
}
}, 0);
}
if (self.status === 'Rejected') {
setTimeout(function () {
try {
var nextRejectValue = onRejected(self.rejectReason);
ResolutionRetrunPromise(nextPromise, nextRejectValue, res, rej);
}catch(e) {
rej(e);
}
}, 0);
}
//
if (self.status === 'pending') {
self.ResolveCallBackList.push(function () {//先将要执行的函数放到数组里,异步执行的时候再执行
try {
var nextResolveValue = onFulfilled(self.resolveValue);
ResolutionRetrunPromise(nextPromise, nextResolveValue, res, rej);
}catch(e) {
rej(e);
}
});
self.RejectCallBackList.push(function () {
setTimeout(function () {
try {
var nextRejectValue = onRejected(self.rejectReason);
ResolutionRetrunPromise(nextPromise, nextRejectValue, res, rej);
}catch(e) {
rej(e);
}
}, 0);
});
}
});
return nextPromise;//要实现链式操作,就要返回一个Promise,便于下一次处理
};
MyPromise.race = function(promiseArr) {
return new MyPromise(function (resolve, reject) {
promiseArr.forEach(function (promise, index) {
promise.then(resolve, reject);//哪个先改变,一定会触发Promise的resolve, reject
});
});
};
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
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
ES6之Iterator
- 迭代的基本概念
- Symbol 第七种数据结构
let os = Symbol('abc');
console.log(os);//输出Symbol(abc)
let os2 = Symbol({
name: 'cst',
toString: function () {//重写了toString方法,要不重写就去找object上的
return 'duyi'
}
});
console.log(os2);//输出Symbol(duyi)
let os2 = Symbol('abc');//Symbol具有唯一性,即使输出一样os2也不等于os
let obj = {
[os2]: 'cst2',//Symbol类型数据结构可以作为属性
[os]: 'cst'//取值只能写obj[os]
}
//具有Symbol.iterator属性的才能用ES6的迭代方法
// itertor函数
console.log( Symbol.iterator , Symbol('Symbol.iterator'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 对没有ES6迭代方法的如何添加
// ES6 规范 iterator
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
//要想没有Symbol.iterator属性的,具有ES6迭代方法,就要手动添加
[Symbol.iterator]: function () {
let curIndex = 0;//要迭代对象的索引,从0开始
let next = () => {
return {
value: this[curIndex],//要迭代对象索引对相应的值
done: this.length == ++curIndex,//索引自动加,直到与要迭代对象长度相同,输出true,表示迭代完成
}
}
return {
next//必须return出next函数,使用ES6方法(... for of等)就要调用iterator方法,即执行next
}
}
};
let arr = [1, 2]
console.log([...obj]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ES6之Generator
- 基本概念
- Generator 生成迭代对象
function *test () {//定义生成器
yield 'a';//oG.next() 输出{value: a , done: false} 并且暂停
console.log('1');
yield 'b';//oG.next() 输出 1 和{value: b , done: false} 并且暂停
console.log('2');
yield 'c';//oG.next() 输出 2 和{value: c , done: false} 并且暂停
console.log('3');
return 'd';//oG.next() 输出 3 和{value: d , done: true}
}
let oG = test();
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 一点应用一
function *test () {
let value1 = yield 'a';//oG.next() 输出{value: a , done: false} 并且暂停
console.log(value1);
let value2 = yield 'b';//oG.next('test1') 输出 test1(value1输出取决与.next() 传入什么) 和{value: b , done: false} 并且暂停
console.log(value2);
let value3 = yield 'c';//oG.next('test2') 输出 test2 和{value: c , done: false} 并且暂停
console.log(value3);
return 'd';//oG.next('test3') 输出 test3 和{value: d , done: true}
}
let oG = test();
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 一点应用二
// 改造添加Symbol.iterator的函数
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: function *() {
let currIndex = 0;
while (currIndex != this.length) {//当变量不等于对象长度,就执行下面
yield this[currIndex];//输出属性值,暂停
currIndex++;//下次先执行这,再往循环
}
}
};
console.log([...obj]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 一点应用三
// node环境下
let fs = require('fs');
function readFile (path) {
return new Promise((res, rej) => {
fs.readFile(path, 'utf-8', (err, data) => {
if (data) {
res(data);
}else {
rej(err);
}
})
});
};
//方法一:利用.then来层层调用
readFile('./data/number.txt').then((val) => {
return readFile(val);
}).then((val) => {
return readFile(val);
}).then((val) => {
console.log(val);
});
// 方法二:Generator 函数
function * read () {
let val1 = yield readFile('./data/number.txt');
let val2 = yield readFile(val1);
let val3 = yield readFile(val2);
return val3;
};
let oG = read();
let {value, done} = oG.next();//解构 {value(Promise对象readFile('./data/number.txt')), done:false}
value.then((val) => {//val拿到值
let {value, done} = oG.next(val);//.next(val)让val1拿值继续执行
value.then((val) => {
let {value, done} = oG.next(val);
value.then((val) => {
console.log(val);
});
});
});
//方法二优化: 递归优化函数
function Co (oIt) {
return new Promise((res, rej) => {
let next = (data) => {//声明一个函数
let {value, done} = oIt.next(data);//解构
if (done) {
res(value);
}else {
value.then((val) => {
next(val);//递归
}, rej);//失败的回调
}
}
next();
});
}
Co( read() ).then((val) => {
console.log( val);
},() => {
//触发失败的回调
})
// co是插件模块,是TJ写的,他还写了 express koa node
let co = require('co');//可下载插件,直接引入
co( read() ).then((val) => {
console.log(val)
});
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
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
ES7之async + await
- 基本概念
- 对上面 ------>一点应用三 的改写 利用async + await
async function read (url) {//async函数异步执行
let val1 = await readFile(url);//await等待readFile(url)执行完再将结果赋值给val1
let val2 = await readFile(val1);
let val3 = await readFile(val2);
return val3;//是Promise对象,还需要.then来触发回调
};
read('./data/number.txt').then((val) => {
console.log(val);
});
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 同步并发的异步结果
//避免像Promise.all一样,出现一次错误就无法输出
async function read1 () {
let val1 = null;
try {
val1 = await readFile('./data/number1.txt');
console.log(val1);
}catch(e) {
console.log(e)
}
};
async function read2 () {
let val2 = null;
try {
val2 = await readFile('./data/number.txt');
console.log(val2);
}catch(e) {
console.log(e, 2)
}
};
async function read3 () {
let val3 = null;
try {
val3 = await readFile('./data/number1.txt');
console.log(val3);
}catch(e) {
console.log(e)
}
};
function readAll(...args) {//遍历所有函数,并执行
args.forEach((ele) => {
ele();
});
}
readAll(read1, read2, read3);
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
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
ES6其它内容扩展(丁老师)
- 模块化
方法一:
导出export {num as n ,str, show, Node} //导出的num起了个别名n 在引入的时候必须引入n
导入import {num ,str, show as pps, Node} from './a.js' //引入的show起了个别名pps 下面在用的时候就可以使用pps
方法二:
默认导出:export default 1 默认导入 import a from './a.js'
方法三:
默认导出:export {num as default ,str, show, Node}
默认导入 import a from './a.js'
方法四:
import * as demo from './a.js' //把文件中所用东西导入