橙子

es6 总结

es6 总结

let、const

在es6之前的作用域分为:全局作用域、函数作用域,而在es6中出现了一个块级作用域

var:变量声明提升、可重复定义、全局变量挂载到 window

let(块级变量声明):

  1 没有变量声明提升、不能重复定义、不挂载到

  2 声明的变量和 {} 配合产生块级作用域 - 生命在大括号内部的变量无法在外部使用

  3 产生临时 Temporal Dead Zone (临时死区)

  4 解决闭包问题(ES6规范后引入的)

const(块级变量声明):

  其存储量的空间不可以被改变、其余和 let 一样

总:先 const 再 let

spreed&rest

… 展开 收集 运算符:

  写:function test (...arg) {}; test(1, 2, 3); —收集作用

  读:var arg = [1, 2, 3]; console.log(...arg); —展开作用

对于这个 … ,es6 中可以处理数组,es7 中可以处理对象

这里还有个方法 Object.assign ,可以克clone,但是是浅clone:

1
2
3
4
5
6
7
8
9
10
let obj1 = {
name: 'vs'
}

let obj2 = {
value: 'more'
}

console.log( Object.assign({}, obj1, obj2) );
// {"name":"vs","value":"more"}

destructuring

解构,也就是结构化赋值,其实我觉得就是两者对比,然后把相同位置的内容赋值过去。当然也可以设置默认值,当对面不存在这个值得时候就用默认值

1
2
3
4
5
6
7
8
9
10
let obj = {
name: 'hemuyi',
age: 18
}

let {name, age, position = 'wh'} = obj

console.log(name, age, position)

// hemuyi 18 wh

箭头函数

箭头函数减少了很多代码量,当然不止这个作用,对于闭包它也可以很好的处理

箭头函数特点:

  1 不用写 function 关键字

  2 只能作为函数使用,不能 new, 没有原型

  3 参数名不能重复

  4 返回值可以不写 return,但要配合 {} 使用

  5 内部 arguments this 由定义时外围最接近一层的非箭头函数的 arguments 和 this 决定其值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function test (a, b) {
return a + b
}

test(1, 2)

// 箭头函数形式,注意这种返回的不是对象

var test2 = (a, b) => a + b
test2(1, 2) // 3

// 箭头函数形式,返回的是对象,需要用 () 把这里变成表达式
var test3 = (a, b) => ({a: a, b: b})
test3(1, 2) // {a: 1, b: 2}

高阶函数的时候:

1
2
3
4
5
6
7
8
9
10
11
12
13
function sum1 (x) {
return function (y) {
return function (z) {
return x + y + z
}
}
}
sum1(1)(2)(3) // 6

// 箭头函数

var sum2 = x => y => z => x + y + z
sum2(1)(2)(3) // 6

总:这个要灵活使用好啊,可以方便很多

ES5 - Object.defineProperty

这个方法可直接在一个对象上定义一个新的具有详细描述的属性,或者修改一个对象的现有属性, 并返回这个对象。

使用:Object.defineProperty(对象,属性,描述符);

使用规则:

  数据描述符:

    value: ‘xxx’属性值。 默认‘’

      writable:true 是否可写。默认false

    configurable:true 是否可配置。默认false

    enumerable:true 是否可枚举。默认false

  存取描述符:

    set:function(){} 属性访问器 进行写操作时调用该方法

    get:function(){} 属性访问器 进行读操作时调用该方法

数据描述符例子:

1
2
3
4
5
6
7
8
let obj2 = Object.defineProperty({}, 'name', {
value: 'he',
writable: true,
configurable: true,
enumerable: true
})

console.log(obj2.name) // 'he'

上面这种是产生一个新对象,但也可以对已定义的对象使用这种方法,但要注意,如果是普通方式定义的对象,则 writable configurable enumerable 这三个属性是有的,并为 true,还有,若是已定义过的属性,则会覆盖

1
2
3
4
5
6
let obj3 = {
name: 'he'
};
Object.defineProperty(obj3, 'name', {
value: 'qi'
})

存取描述符例子:

1
2
3
4
5
6
7
8
9
10
11
12
let nameVal = 'he'
let obj4 = Object.defineProperty({}, 'name', {
configurable: true,
enumerable: true,
get () {
return nameVal
},
set (val) {
nameVal = val
}
})
obj4.name = 'qi'

一般对象中也可以使用 get set ,如下,要注意是下面这样的,所以在一般对象中定义时谨慎使用,也可能是我对这种还不太熟悉。。。

1
2
3
4
5
6
7
8
9
10
11
12
var obj5 = {
val: 'hemuyi',
get name () {
return this.val
},
set name (val) {
this.val = val
}
}
console.log(obj) // {"val":"he","name":"he"}
obj5.name = 10;
console.log(obj5) // {"val":10,"name":10}

ES5 - 数据劫持

我觉得就是数据监控的过程,然后在监控到数据变化时做一些处理

es5 中数据劫持的例子:

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
<input type="text" id='demo'/>
<div id='show'></div>
<script>
var oDiv = document.getElementById('show');
var oInput = document.getElementById('demo');

var oData = {
valueObj: {
value: 'duyi',
name: 'haha'
},
sex: 'aaa'
};

// var arr = [];

oInput.oninput = function () {
oData.value = this.value;
};


function upDate () {
oDiv.innerText = oData.valueObj.value;
}

upDate();

// 监控对象的某个属性是否发生改变
function Observer (data) {
if (!data || typeof data != 'object') {
return data;
};
// for (var prop in data) {

// }
Object.keys(data).forEach(function (key) {
definedRective(data, key, data[key]);
});
}

function definedRective (data, key, val) {
// AO
Observer(val); //递归, 可能对象的某属性依旧是个对象
Object.defineProperty(data, key, {
get () {
// console.log('get');
return val;
},
set (newValue) {
if (newValue == val) return;
val = newValue;
upDate();
}
})
};

Observer(oData);

console.log(oData.value);
oData.valueObj.value = 'haha';

oData.aa = 10;
</script>

这样就实现了数据监控,进而双向绑定,只能对对象这样操作,但是,但是,当在 Observer(oData); 后给 对象 oData 进行 oData.aa = 10 ,此时并不会监控到这个操作,这个属性是后加的,Object.defineProperty 并不能解决这个问题,要解决这个问题,请看 Proxy Reflect

而在 vue 中稍微有点不同, 若是要监控数组,大体是这样实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let arr = [];
let {push} = Array.prototype;
function upData () {
console.log('更新');
};
// [
// 'push',
// 'pop'
// ]
// arr push pop unshift shift slice....
Object.defineProperty(Array.prototype, 'push', {
value: (function () {
return (...arg) => {
push.apply(arr, arg);
upData();
}
})()
});


arr.push(1, 2);

至此,就可以对这个数组的push方法进行监控了,vue 中大体是这样实现的

ES6 - Proxy Reflect

首先,这两个目前兼容性不是很好

例子:

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
let oData = {
val: 'duyi',
_val: 'aaaa'
}

let oProxyData = new Proxy(oData, {
set (target, key, value, receiver) {
// console.log(target, key, value, receiver);
Reflect.set(target, key, value);
upDate();
},
get (target, key, receiver) {
// console.log(target, key, receiver);
// return target[key]
return Reflect.get(target, key);
},
has (target, key) {
return key.indexOf('_') != -1 ? false : key in oData;
},
deleteProperty () {

}
});

console.log( delete oProxyData.val );

// 读写 控制
console.log( oProxyData.val );
oProxyData.val = 10;

function upDate () {
console.log('更新了')
}

oProxyData.name = 20;

总:监控到对象的变化,哪怕是后来新加的属性

ES6 - Class

基本内容:

class 是构造函数的一种写法;方法写在原型上;不具有函数声明整体提升(明确遵守先定义后使用)

constructor 私有属性、 公有属性(原型上的属性)、 静态属性(函数属性):

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
class Plane1 {
// es7 写法 -- 静态属性
// static alive = true
// es6
static alive () {
return true
}
// 私有属性
constructor (name) {
this.name = name;
this.age = 18;
}
// es7 写法 -- 私有属性
// po = 'wh'
// 公有属性
fly () {
console.log('fly')
}
}

Plane(); // 报错,必须 new
Plane.alive() // true
let oP1 = new Plane1('qi')
oP1.name // qi
oP1.fly() // fly

总:

  1 constructor 里的是私有属性,如:name age

  2 公有属性(原型上),如:fly

  3 静态属性并不是运行在 oP,而是函数本身 Plane

extends,继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Plane2 extends Plane1 {
constructor (name) {
super() // 类似于 Plane1.call()
this.logo = 'yi'
}
speed () {
console.log('sousousou')
}
}

let oP2 = new Plane2('he')
Plane2.alive() // true
oP2.name // he
oP2.logo // yi
oP2.speed() // sousousou

总:

  1 class 的构造函数必须 new

  2 在 constructor 里必须使用 super()

  3 extends 继承的是公有属性 和 静态属性,私有的通过给 constructor 传参来实现

  4 Plane1.prototype 及 静态属性 不能枚举

模拟 Class

1 定义 Plane :

1
2
3
4
function Plane() {

}
new Plane()

2 必须 new 才能执行,没有 new 的话 this是window

1
2
3
4
5
function _classCallCheck (_this, _constructor) {
if (! (_this instanceof _constructor) ) {
throw "TypeError: Class constructor Plane cannot be invoked without 'new'";
}
};

3 若是按照1 中的方式定义函数,则是暴露在全局,所以用立即执行函数来定义

1
2
3
4
5
6
7
8
var Plane = (function () {
function Plane (name) {
// 判断是否以new的方式来执行
_classCallCheck(this, Plane);
}
return Plane;
})();
var oP = new Plane();

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
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 _createClass (_constructor, _prototypeProperties, _staticProperties) {
// 给原型上赋值
if (_prototypeProperties) {
_defineProperties(_constructor.prototype, _prototypeProperties);
}
if (_staticProperties) {
_defineProperties(_constructor, _staticProperties);
}
}
function _defineProperties (target, props) {
// Object.defineProperty
props.forEach(function (ele) {
// ele.key ele.value
Object.defineProperty(target, ele.key, {
value: ele.value,
writable: true,
configurable: true
});
})
}

var Plane = (function () {
function Plane (name) {
// 判断是否以new的方式来执行
_classCallCheck(this, Plane);
this.name = name || '普通飞机';
this.blood = 100;
}

_createClass(Plane, [
{
key: 'fly',
value: function () {
console.log('fly');
}
}
], [
{
key: 'alive',
value: function () {
return true;
}
}
]);

return Plane;
})();

extends:

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
function _inherit (sub, sup) {
Object.setPrototypeOf(sub.prototype, sup.prototype);
}

var AttackPlane = (function (Plane) {

_inherit(AttackPlane, Plane);
function AttackPlane (name) {
_classCallCheck(this, Plane);
var _this = this;
var that = Plane.call(_this, name);
if (typeof that == 'object') {
_this = that;
}
_this.logo = 'qi';
return _this;
};

_createClass(AttackPlane, [
{
key: 'dan',
value: function () {
console.log('biubiubiu');
}
}
], [
{
key: 'alive',
value: function () {
return true;
}
}
]);

return AttackPlane;
})(Plane);


var oAp = new AttackPlane();

ES7 - Class

es7 中的 class 有些是高版本的浏览器也不支持,需要一些工具来降级才能用

静态属性、私有属性用的工具:

  1 安装 :npm install @babel/plugin-proposal-decorators

  2 配置:”plugins”: [ [“@babel/plugin-proposal-class-properties”, { “loose” : true }] ]

静态属性、私有属性的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Search {
// ES7 静态属性
static num = 10;
// ES6
// static num () {
// return 6;
// }
constructor () {
this.keyValue = '';
}
// 私有属性
age = 18
getCount () {
console.log('发送请求');
}
};

var oS = new Search();
console.log(oS);

装饰器 :@decorator:

安装的降级工具:npm install @babel/plugin-proposal-decorators

配置:”plugins”: [ [“@babel/plugin-proposal-decorators”, { “legacy”: 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
// 私有属性
class Search {
constructor () {
this.keyValue = '';
}
@myReadOnly
url = 'urlA-';

@dealData('张三')
getContent (a, b) {
console.log('向' + this.url + '发送网络请求, 数据:' + this.keyValue, a, b);
}
};

function Skin (target) {
target.aaa = 30;
}
// 装饰私有属性 descriptor 是没有 value 的,initializer
function myReadOnly (proto, key, descriptor) {
// console.log(proto, key, descriptor);
descriptor.writable = false;
// descriptor.initializer(); 值为 url 的值 'urlA-',修改的话按下面的方法
descriptor.initializer = function () {
return 6;
}
}

// 原型上的属性的话 descriptor 是有value的, 指的是getContent
function dealData (proto, key, descriptor) {
console.log(proto, key, descriptor);
let oldValue = descriptor.value;
// 代理思想
descriptor.value = function () {

var urlB = 'urlB-';
console.log('向' + urlB + '发送网络请求, 数据:' + this.keyValue);

return oldValue.apply(this, arguments);
}
}

修饰类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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.aaa = 30; // 静态属性
}

总:感觉 es7 掌握的还不够,还差很多,还需努力啊

Set Map

首先,set map 兼容性并不是很好

Set

set 是es6提供的构造函数,用来存储数据的结构,当然用数组 对象也可以存储数据,但是有些时候实现某种功能会稍显麻烦,比如数组去重

定义:

  参数是具备迭代接口的,如数组、对象、arguments

1
2
let oS = new Set([1, 2, 3, 1, 'a', [5, 6]])    
let oS2 = new Set('absdefg')

  这两个值是这样的:

增、删、枚举、清空、包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let obj = {
name: 'he'
}
let oS = new Set([1, 2, 3, 1, 'a', [5, 6]])

oS.add(1)
oS.add(obj)

// 删除数组时需要记录下这个数组,否则不知道删除的是谁
oS.delete(obj)

oS.has(1)

oS.forEach( value => {
console.log(value)
})
//es6 for of 具备迭代接口的
for(let value of oS) {
console.log(value)
}

oS.clear()
console.log(oS)

数组 与 Set 互相转换:

1
2
3
4
5
6
let arr = [1, 2, 3, 4]
let oS = new Set(arr)

let newArr1 = Array.from(oS)
let newArr2 = [...oS]
console.log(newArr1, newArr2)

两个数组的 交集、并集、差集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let arr1 = [1, 2, 3, 4]
let arr2 = [3, 4, 5, 6]

// 并集 [1, 2, 3, 4, 5, 6]
let oS1 = new Set([...arr1, ...arr2])

// 交集 [3, 4]
let oS2 = new Set(arr1)
let oS3 = new Set(arr2)
let newArr1 = [...oS2].filter((ele, index) => oS3.has(ele))

// 差集 [1, 2, 5, 6]
let newArr2 = [...oS2].filter((ele, index) => !oS3.has(ele))
let newArr3 = [...oS3].filter((ele, index) => !oS2.has(ele))
let newArr4 = [...newArr2, ...newArr3]

Map

map 同样是 es6 提供的构造函数,能够存储数据,本质上是键值对的集合。

定义:

1
let oMp = new Map([['name', 'he'],['age', 18], ['po', 'wh']])

它的结构是这样的:

增、查、删、包含、枚举、清空、长度:

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
let oMp = new Map([['name', 'he'],['age', 18], ['po', 'wh']])
let obj = {
name: 'qi'
}
oMp.set('wea', 'yu')
oMp.set(obj)
oMp.set({}, '------')
oMp.set({}, '++++++')

oMp.get('name')

oMp.forEach((value, index) => {
console.log(value)
})
for(let val of oMp) {
console.log(val)
}

oMp.has('name')

oMp.delete('name')
oMp.delete(obj)
oMp.delete({}) // 删除失败,必须是已确定的或是原始值才可以

oMp.clear()

oMp.size // 0

链表、hashsuanfa、桶

链表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{} {} {} {} {}
let node3 = {
key: 'name3',
value: '3',
next: null
};

let node2 = {
key: 'name2',
value: '2',
next: node3
};

let node1 = {
key: 'name',
value: '1',
next: node2
};

hash 、桶:

1
2
3
4
let oMp = new Map([['name1', '1'], ['name2', '2']]);

// hash ->
// {} 'name1' 'age' 1 true NaN

hash: 就是把不定范围的值转化成特定范围的

桶:就是容器(具体的我描述不出来)

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
// 明显散列的
{
key: 'name1',
value: '1',
}
{
key: 'name2',
value: '2',
}

// hash 之后, 桶 就是外面这层数组
[
{
next: {
key: 'name1',
value: '1',
next: {
key: 'name2',
value: '2'
}
}
},
{},
{},
{},
{},
{},
{},
{}
];

大体的图是这样的:

模拟 Map

map有一下几个特点:

  1 不重复

  2 字符串 对象 NaN null [] function(){} 10

  3 set get delete has clear

模拟过程如下:

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
function myMap () {
this.bucketLength = 8;
this.init();
}

myMap.prototype.init = function () {
// 初始化 桶 8
this.bucket = new Array( this.bucketLength );
for (var i = 0; i < this.bucket.length; i++) {
this.bucket[i] = {
type: 'bucket_' + i,
next: null
}
}
}
//
// 1. [0, 8)
// 2. 重复算值固定
myMap.prototype.makeHash = function (key) {
let hash = 0;
// string
if (typeof key !== 'string') {
if (typeof key == 'number') {
//number NaN
hash = Object.is(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[]
hash += key[i] ? key[i].charCodeAt(0) : 0;
}
}
return hash % 8;
}

myMap.prototype.set = function (key, value) {
let hash = this.makeHash(key);
let oTempBucket = this.bucket[hash];
while (oTempBucket.next) {
if (oTempBucket.next.key == key) {
oTempBucket.next.value = value;
return;
}else {
oTempBucket = oTempBucket.next;
}
};
oTempBucket.next = {
key: key,
value: value,
next: null
};
}

myMap.prototype.get = function (key) {
let hash = this.makeHash(key);
let oTempBucket = this.bucket[hash];
while(oTempBucket) {
if (oTempBucket.key == key) {
return oTempBucket.value;
}else {
oTempBucket = oTempBucket.next;
}
}
return 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;
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', 'he');
oMp.set('name2', 'qi');
oMp.set(obj1, '---');
oMp.set(obj1, '+++');
oMp.set(function () {}, true);

​ 总:这个过程适合反复练习

Promise

promise 是一个内置的构造函数

promise 基本使用:
then 注册函数返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 注意 执行顺序
setTimeout(() => {
console.log('settime')
}, 0);
let oP = new Promise((resolve, refect) => {
// 异步操作
setTimeout(() => {
Math.random() * 100 > 60 ? resolve('ok') : refect('no')
}, 600);
})
console.log(0)
// 异步执行
oP.then( (val) => {
console.log(val)
}, (reason) => {
console.log(reason)
} )
console.log(1)

// 0 1 settime no

在 then 中成功的回调和失败的回调有几个特点,如下代码:

  1 可以链式调用

  2 若是没有抛出错误那下一个 then 调用的是成功的回调,若是抛出错误了则是错误的回调,无所谓当前调用的回调是成功还是失败,只关心回调有没有抛出错误

  3 若回调 return 的是 promise,则看这个 promise 是调用成功还是错误的回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let oP = new Promise((resolve, refect) => {
// 异步操作
setTimeout(() => {
Math.random() * 100 > 60 ? resolve('ok') : refect('no')
}, 600);
})

oP.then( (res) => {
console.log(res)
// throw new Error('qi')
// return new Promise((resolve, reject) => {
// reject('newPromise ok');
// });
// return 20
}, (rej) => {
console.log(rej)
throw new Error('he')
// return 30
}).then( (res) => {
console.log('ok: then: ' + res)
}, (rej) => {
console.log('no: then: ' + rej)
})

catch 异常捕获 finally 最后处理函数

then 捕获到错误后不再执行catch,但catch捕获到错误后会执行then;链式调用的时候如果写一个空then其实相当于不存在可以忽视

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
let oP = new Promise((resolve, refect) => {
// 异步操作
setTimeout(() => {
Math.random() * 100 > 60 ? resolve('ok') : refect('no')
}, 600);
})

// 捕获到错误后下一个then抛出了,然后后面的catch 没有捕获到
oP.then( (res) => {
console.log(res)
throw new Error('qi')
}, rej => {
console.log(rej)
throw new Error('he')
} ).then().then( (res) => {
console.log('ok-then: ' + res)
}, rej => {
console.log('no-then: ' + rej)
} ).catch( err => {
console.log( err)
} )

// catch 捕获到错误后,后面的then仍能捕获到这个错误
oP.then( (res) => {
console.log(res)
throw new Error('qi')
}, rej => {
console.log(rej)
throw new Error('he')
} ).then().then( () => {

}).catch( err => {
console.log( err)
} ).then( (res) => {
console.log('catch后-ok: ' + res)
}, rej => {
console.log('catch后-no:' + rej)
} )

finally 终结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
oP.then( (res) => {
console.log(res)
throw new Error('qi')
}, rej => {
console.log(rej)
throw new Error('he')
} ).then().then( () => {

}).catch( err => {
console.log( err)
} ).then( (res) => {
console.log('catch后-ok: ' + res)
}, rej => {
console.log('catch后-no:' + rej)
} ).finally( () => {
console.log('over')
} )

Promise.all Promise.race

Promise.all :同步并发异步结果。Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,都成功的时候返回的是一个结果数组(调用成功的回调),而失败的时候则返回最先被reject失败状态的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
});

Promise.race:谁先处理成功就处理谁。Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果, 不管结果本身是成功状态还是失败状态。有一个失败就触发失败

1
2
3
4
5
6
7
8
9
10
11
12
function test (x) {
return new Promise((res, rej) => {
setTimeout(() => {
Math.random() * 100 > 50 ? res(x) : rej(x);
}, 100)
});
}
Promise.race([test('a'), test('b'), test('c')]).then((val) => {
console.log(val, 'ok');
}, (reason) => {
console.log(reason, 'no');
});

Promise 原理

then 的用法及要求可以在这个网站看,很合适 Promise A+规范

算了,上代码吧,这里适合多次练习

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
let oP = new MyPromise( (res, rej) => {
// throw new Error('duyi');
// 异步操作
// setTimeout(() => {
// res(0);
// }, 2000);
// console.log(0);
res(1);
});

// myPromise.js
function MyPromise (executor) {
var self = this;
self.status = 'pending';
self.resolveValue = null;
self.rejectReason = null;
self.ResolveCallBackList = [];
self.RejectCallBackList = [];

function resolve (value) {
if (self.status === 'pending') {
self.status = 'Fulfilled';
self.resolveValue = value;
self.ResolveCallBackList.forEach(function (ele) {
ele();
});
}
}

function reject (reason) {
if (self.status === 'pending') {
self.status = 'Rejected';
self.rejectReason = reason;
self.RejectCallBackList.forEach(function (ele) {
ele();
});
}
}

try {
executor(resolve, reject);
}catch(e) {
reject(e);
}
};
function ResolutionRetrunPromise (nextPromise, returnValue, res, rej) {
if (returnValue instanceof MyPromise) {
// Promise 对象
returnValue.then(function (val) {
res(val);
}, function (reason) {
rej(reason)
});
}else {
res(returnValue);
}
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
if (!onFulfilled) {
onFulfilled = function (val) {
return val;
}
}
if (!onRejected) {
onRejected = function (reason) {
throw new Error(reason);
}
}
var self = this;

var nextPromise = new MyPromise(function (res, rej) {
if (self.status === 'Fulfilled') {
setTimeout(function () {
try {
// var nextResolveValue = onFulfilled(self.resolveValue);
// res(nextResolveValue);
var nextResolveValue = onFulfilled(self.resolveValue);
ResolutionRetrunPromise(nextPromise, nextResolveValue, res, rej);
}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;
};

MyPromise.race = function(promiseArr) {
return new MyPromise(function (resolve, reject) {
promiseArr.forEach(function (promise, index) {
promise.then(resolve, reject);
});
});
};

Iterator

迭代模式:
​   提供一种方法可以顺序获得聚合对象中的各个元素,是一种最简单也最常见的设计模式。它可以让用
户透过特定的接口巡访集合中的每一个元素而不用了解底层的实现。

迭代器简介:
​   依照与迭代模式的思想而实现,分为内部迭代器和外部迭代器。

内部迭代器:本身是函数,该函数内部定义好迭代规则,完全接手整个迭代过程,外部只需要一次初始调用。
​   如:Array.prototype.forEach 、jQuery.each 内部迭代器

外部迭代器:本身是函数,执行返回迭代对象,迭代下一个元素必须显示调用,调用复杂度增加,但灵活性增强。

  如:function outerItreator(){} 外部迭代器

能够被迭代的数据,其原型上有 Symbol.iterator,如数组 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
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: function () {
let curIndex = 0;
let next = () => {
return {
value: this[curIndex],
done: this.length == ++curIndex,
}
}
return {
next
}
}
};

let arr = [1, 2]
console.log([...obj]);

for (let p of obj) {
console.log(p)
};

####

Symbol

第七种数据结构 Symbol。特点是具有唯一性

例子:

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
let os = Symbol('abc');
console.log(os);
let os2 = Symbol({
name: 'cst',
toString: function () {
return 'qi'
}
});
console.log(os2);

//
let os2 = Symbol('abc'); // os os2 并不是同一个

//
let prop = 'name';

let obj = {
[os2]: 'cst2',
[os]: 'cst'
}
// 取值用 obj[os]

// arr set map arguments nodelist
// itertor函数
console.log( Symbol.iterator , Symbol('Symbol.iterator'));

ES6 - Generator

genarator 是迭代生成器。本身是函数,执行后返回迭代对象,函数内部要配合yield使
用Generator函数会分段执行,遇到yield即暂停。

generator 特点:

  1 function和函数名之间需要带 *

  2 函数体内部yield表达式,产出不同的内部状态(值)

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
// Generator 生成这个迭代对象

function *test () {
yield 'a';
console.log('1');
yield 'b';
console.log('2');
yield 'c';
console.log('3');
return 'd';
}
let oG = test();

console.log( oG.next() );
console.log( oG.next() );
console.log( oG.next() );
console.log( oG.next() );


function *test () {

let value1 = yield 'a';

console.log(value1);
let value2 = yield 'b';

console.log(value2);
let value3 = yield 'c';

console.log(value3);
return 'd';
}

let oG = test();
//
console.log( oG.next('yi') ); // 啥都不会打印
oG.next('qi'); // yi
oG.next('he'); // qi
oG.next('mu'); // he

总:上面代码中最后打印的内容 取决于next()中传的东西,而不是产出的东西

迭代器代码修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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++;
}
}
};