# ECMAScript
TIP
ECMAScript
# 一、var 和 let
- JS 中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称 ES6)中新增了块级作用域
- 块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作用域
# 全局变量
在 if 或者 for 循环中声明的变量会变成全局变量
for (var i = 0; i <= 5; i++) {
console.log("hello");
}
console.log(i); //5
1
2
3
4
2
3
4
内层变量可能会覆盖外层变量
var a = 1;
function fn() {
console.log(a);
if (false) {
var a = 2;
}
}
fn(); //undefined
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# let
- 允许块级作用域任意嵌套
- 外层作用域无法读取内层作用域的变量
- 内层作用域可以定义外层作用域的同名变量
- 函数本身的作用域在其所在的块级作用域之内
"use strict";
function fn() {
console.log("out");
}
(function () {
if (false) {
function fn() {
console.log("in");
}
}
fn();
})();
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# var&let&const
var 定义的变量没有块的概念,可以跨块访问,不能跨函数访问,有变量提升,可重复声明
let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明
let 声明的变量只在块级作用域内有效,不存在变量提升,而是绑定在暂时性死区 或者说 let 变量提升了,但是在 let 声明变量前不能使用该变量,这特性叫暂时性死区(temporal dead zone) 如果有重复变量 let 会在编译阶段报错
暂时性死区
// 不存在变量提升'use strict';
function func() {
console.log(i);
let i;
}
func(); // 报错
1
2
3
4
5
6
2
3
4
5
6
# ES5 声明变量只有两种方式:var 和 function
ES6 有 let、const、import、class 再加上 ES5 的 var、function 共有六种声明变量的方式 浏览器环境中顶层对象是 window,Node 中是 global 对象 ES5 中 顶层对象的属性等价于全局变量 ES6 中 var、function 声明的全局变量,依然是顶层对象的属性;let、const、class 声明的全局变量不属于顶层对象的属性
"use strict";
var a = 1;
console.log(a); //1
{
console.log(a); // f 1
function a() {
console.log(1);
}
}
console.log(a); // f 1
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 二、解构
# 数组的解构
// 数组的解构
const arr = [100, 200, 300];
// const foo = arr[0]
// const bar = arr[1]
// const baz = arr[2]
// console.log(foo, bar, baz)
// const [foo, bar, baz] = arr
// console.log(foo, bar, baz)
// const [, , baz] = arr
// console.log(baz)
// const [foo, ...rest] = arr
// console.log(rest)
// const [foo, bar, baz, more] = arr
// console.log(more)
// const [foo, bar, baz = 123, more = 'default value'] = arr
// console.log(bar, more)
const path = "/foo/bar/baz";
// const tmp = path.split('/')
// const rootdir = tmp[1]
const [, rootdir] = path.split("/");
console.log(rootdir);
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
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
# 对象的解构
// 对象的解构
const obj = { name: "zce", age: 18 };
// const { name } = obj
// console.log(name)
// const name = 'tom'
// const { name: objName } = obj
// console.log(objName)
// const name = 'tom'
// const { name: objName = 'jack' } = obj
// console.log(objName)
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
# 三 、 模板字符串
// 模板字符串
// 反引号包裹
// const str = `hello es2015, this is a string`
// 允许换行
// const str = `hello es2015,
// this is a \`string\``
// console.log(str)
const name = "tom";
// 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置
const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`;
console.log(msg);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 四、 函数参数的默认值
// 函数参数的默认值
// function foo (enable) {
// // 短路运算很多情况下是不适合判断默认参数的,例如 0 '' false null
// // enable = enable || true
// enable = enable === undefined ? true : enable
// console.log('foo invoked - enable: ')
// console.log(enable)
// }
// 默认参数一定是在形参列表的最后
function foo(enable = true) {
console.log("foo invoked - enable: ");
console.log(enable);
}
foo(false);
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
# 五、 剩余参数
// 剩余参数
// function foo () {
// console.log(arguments)
// }
function foo(first, ...args) {
console.log(args);
}
foo(1, 2, 3, 4);
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 六、 展开数组参数
// 展开数组参数
const arr = ["foo", "bar", "baz"];
// console.log(
// arr[0],
// arr[1],
// arr[2],
// )
// console.log.apply(console, arr)
console.log(...arr);
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
# 七、 箭头函数
// 箭头函数
// function inc (number) {
// return number + 1
// }
// 最简方式
// const inc = n => n + 1
// 完整参数列表,函数体多条语句,返回值仍需 return
const inc = (n, m) => {
console.log("inc invoked");
return n + 1;
};
console.log(inc(100));
const arr = [1, 2, 3, 4, 5, 6, 7];
// arr.filter(function (item) {
// return item % 2
// })
// 常用场景,回调函数
arr.filter((i) => i % 2);
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 八、 展开数组参数
// 展开数组参数
const arr = ["foo", "bar", "baz"];
// console.log(
// arr[0],
// arr[1],
// arr[2],
// )
// console.log.apply(console, arr)
console.log(...arr);
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
# 九、 箭头函数与 this
- 箭头函数不会改变 this 指向
- 箭头函数没有 this 机制
// 箭头函数与 this
// 箭头函数不会改变 this 指向
const person = {
name: "tom",
// sayHi: function () {
// console.log(`hi, my name is ${this.name}`)
// }
sayHi: () => {
console.log(`hi, my name is ${this.name}`);
},
sayHiAsync: function () {
// const _this = this
// setTimeout(function () {
// console.log(_this.name)
// }, 1000)
console.log(this);
setTimeout(() => {
// console.log(this.name)
console.log(this);
}, 1000);
},
};
person.sayHiAsync();
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
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
# 十、 对象字面量
// 对象字面量
const bar = "345";
const obj = {
foo: 123,
// bar: bar
// 属性名与变量名相同,可以省略 : bar
bar,
// method1: function () {
// console.log('method111')
// }
// 方法可以省略 : function
method1() {
console.log("method111");
// 这种方法就是普通的函数,同样影响 this 指向。
console.log(this);
},
// Math.random(): 123 // 不允许
// 通过 [] 让表达式的结果作为属性名
[bar]: 123,
};
// obj[Math.random()] = 123
console.log(obj);
obj.method1();
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
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
# 十一、 Object.assign 方法
// Object.assign 方法
const source1 = {
a: 123,
b: 123,
};
const source2 = {
b: 789,
d: 789,
};
const target = {
a: 456,
c: 456,
};
const result = Object.assign(target, source1, source2);
console.log(target);
// console.log(result === target)
// 应用场景
function func(obj) {
// obj.name = 'func obj'
// console.log(obj)
const funcObj = Object.assign({}, obj);
funcObj.name = "func obj";
console.log(funcObj);
}
const obj = { name: "global obj" };
func(obj);
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
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
# 十二、 Object.is
// Object.is
console
.log
// 0 == false // => true
// 0 === false // => false
// +0 === -0 // => true
// NaN === NaN // => false
// Object.is(+0, -0) // => false
// Object.is(NaN, NaN) // => true
();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 十二、 Object.is
// Object.is
console
.log
// 0 == false // => true
// 0 === false // => false
// +0 === -0 // => true
// NaN === NaN // => false
// Object.is(+0, -0) // => false
// Object.is(NaN, NaN) // => true
();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 十三、Proxy 对象
// Proxy 对象
// const person = {
// name: 'zce',
// age: 20
// }
// const personProxy = new Proxy(person, {
// // 监视属性读取
// get (target, property) {
// return property in target ? target[property] : 'default'
// // console.log(target, property)
// // return 100
// },
// // 监视属性设置
// set (target, property, value) {
// if (property === 'age') {
// if (!Number.isInteger(value)) {
// throw new TypeError(`${value} is not an int`)
// }
// }
// target[property] = value
// // console.log(target, property, value)
// }
// })
// personProxy.age = 100
// personProxy.gender = true
// console.log(personProxy.name)
// console.log(personProxy.xxx)
// Proxy 对比 Object.defineProperty() ===============
// 优势1:Proxy 可以监视读写以外的操作 --------------------------
// const person = {
// name: 'zce',
// age: 20
// }
// const personProxy = new Proxy(person, {
// deleteProperty (target, property) {
// console.log('delete', property)
// delete target[property]
// }
// })
// delete personProxy.age
// console.log(person)
// 优势2:Proxy 可以很方便的监视数组操作 --------------------------
// const list = []
// const listProxy = new Proxy(list, {
// set (target, property, value) {
// console.log('set', property, value)
// target[property] = value
// return true // 表示设置成功
// }
// })
// listProxy.push(100)
// listProxy.push(100)
// 优势3:Proxy 不需要侵入对象 --------------------------
// const person = {}
// Object.defineProperty(person, 'name', {
// get () {
// console.log('name 被访问')
// return person._name
// },
// set (value) {
// console.log('name 被设置')
// person._name = value
// }
// })
// Object.defineProperty(person, 'age', {
// get () {
// console.log('age 被访问')
// return person._age
// },
// set (value) {
// console.log('age 被设置')
// person._age = value
// }
// })
// person.name = 'jack'
// console.log(person.name)
// Proxy 方式更为合理
const person2 = {
name: "zce",
age: 20,
};
const personProxy = new Proxy(person2, {
get(target, property) {
console.log("get", property);
return target[property];
},
set(target, property, value) {
console.log("set", property, value);
target[property] = value;
},
});
personProxy.name = "jack";
console.log(personProxy.name);
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
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
# 十四、 Reflect 对象
// Reflect 对象
// const obj = {
// foo: '123',
// bar: '456'
// }
// const proxy = new Proxy(obj, {
// get (target, property) {
// console.log('watch logic~')
// return Reflect.get(target, property)
// }
// })
// console.log(proxy.foo)
const obj = {
name: "zce",
age: 18,
};
// console.log('name' in obj)
// console.log(delete obj['age'])
// console.log(Object.keys(obj))
console.log(Reflect.has(obj, "name"));
console.log(Reflect.deleteProperty(obj, "age"));
console.log(Reflect.ownKeys(obj));
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
# 十五、 class 关键词
// class 关键词
// function Person (name) {
// this.name = name
// }
// Person.prototype.say = function () {
// console.log(`hi, my name is ${this.name}`)
// }
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi, my name is ${this.name}`);
}
}
const p = new Person("tom");
p.say();
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
# 十六、 static 方法
// static 方法
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi, my name is ${this.name}`);
}
static create(name) {
return new Person(name);
}
}
const tom = Person.create("tom");
tom.say();
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
# 十七、 extends 继承
// extends 继承
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi, my name is ${this.name}`);
}
}
class Student extends Person {
constructor(name, number) {
super(name); // 父类构造函数
this.number = number;
}
hello() {
super.say(); // 调用父类成员
console.log(`my school number is ${this.number}`);
}
}
const s = new Student("jack", "100");
s.hello();
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
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
# 十八、 extends 继承
// extends 继承
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi, my name is ${this.name}`);
}
}
class Student extends Person {
constructor(name, number) {
super(name); // 父类构造函数
this.number = number;
}
hello() {
super.say(); // 调用父类成员
console.log(`my school number is ${this.number}`);
}
}
const s = new Student("jack", "100");
s.hello();
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
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
# 十九、 Set 数据结构
// Set 数据结构
const s = new Set();
s.add(1).add(2).add(3).add(4).add(2);
// console.log(s)
// s.forEach(i => console.log(i))
// for (let i of s) {
// console.log(i)
// }
// console.log(s.size)
// console.log(s.has(100))
// console.log(s.delete(3))
// console.log(s)
// s.clear()
// console.log(s)
// 应用场景:数组去重
const arr = [1, 2, 1, 3, 4, 1];
// const result = Array.from(new Set(arr))
const result = [...new Set(arr)];
console.log(result);
// 弱引用版本 WeakSet
// 差异就是 Set 中会对所使用到的数据产生引用
// 即便这个数据在外面被消耗,但是由于 Set 引用了这个数据,所以依然不会回收
// 而 WeakSet 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。
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
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
# 二十、 Map 数据结构
// Map 数据结构
// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{ a: 1 }] = 'value'
// console.log(Object.keys(obj))
// console.log(obj['[object Object]'])
const m = new Map();
const tom = { name: "tom" };
m.set(tom, 90);
console.log(m);
console.log(m.get(tom));
// m.has()
// m.delete()
// m.clear()
m.forEach((value, key) => {
console.log(value, key);
});
// 弱引用版本 WeakMap
// 差异就是 Map 中会对所使用到的数据产生引用
// 即便这个数据在外面被消耗,但是由于 Map 引用了这个数据,所以依然不会回收
// 而 WeakMap 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。
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
# 二十一、 Symbol 数据类型
// Symbol 数据类型
// 场景1:扩展对象,属性名冲突问题
// // shared.js ====================================
// const cache = {}
// // a.js =========================================
// cache['a_foo'] = Math.random()
// // b.js =========================================
// cache['b_foo'] = '123'
// console.log(cache)
// =========================================================
// const s = Symbol()
// console.log(s)
// console.log(typeof s)
// 两个 Symbol 永远不会相等
// console.log(
// Symbol() === Symbol()
// )
// Symbol 描述文本
// console.log(Symbol('foo'))
// console.log(Symbol('bar'))
// console.log(Symbol('baz'))
// 使用 Symbol 为对象添加用不重复的键
// const obj = {}
// obj[Symbol()] = '123'
// obj[Symbol()] = '456'
// console.log(obj)
// 也可以在计算属性名中使用
// const obj = {
// [Symbol()]: 123
// }
// console.log(obj)
// =========================================================
// 案例2:Symbol 模拟实现私有成员
// a.js ======================================
const name = Symbol();
const person = {
[name]: "zce",
say() {
console.log(this[name]);
},
};
// 只对外暴露 person
// b.js =======================================
// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
// person[Symbol()]
person.say();
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
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
# 二十二、 Symbol 数据类型
// Symbol 数据类型
// 场景1:扩展对象,属性名冲突问题
// // shared.js ====================================
// const cache = {}
// // a.js =========================================
// cache['a_foo'] = Math.random()
// // b.js =========================================
// cache['b_foo'] = '123'
// console.log(cache)
// =========================================================
// const s = Symbol()
// console.log(s)
// console.log(typeof s)
// 两个 Symbol 永远不会相等
// console.log(
// Symbol() === Symbol()
// )
// Symbol 描述文本
// console.log(Symbol('foo'))
// console.log(Symbol('bar'))
// console.log(Symbol('baz'))
// 使用 Symbol 为对象添加用不重复的键
// const obj = {}
// obj[Symbol()] = '123'
// obj[Symbol()] = '456'
// console.log(obj)
// 也可以在计算属性名中使用
// const obj = {
// [Symbol()]: 123
// }
// console.log(obj)
// =========================================================
// 案例2:Symbol 模拟实现私有成员
// a.js ======================================
const name = Symbol();
const person = {
[name]: "zce",
say() {
console.log(this[name]);
},
};
// 只对外暴露 person
// b.js =======================================
// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
// person[Symbol()]
person.say();
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
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
# 二十三、 for...of 循环
// for...of 循环
const arr = [100, 200, 300, 400];
// for (const item of arr) {
// console.log(item)
// }
// for...of 循环可以替代 数组对象的 forEach 方法
// arr.forEach(item => {
// console.log(item)
// })
// for (const item of arr) {
// console.log(item)
// if (item > 100) {
// break
// }
// }
// forEach 无法跳出循环,必须使用 some 或者 every 方法
// arr.forEach() // 不能跳出循环
// arr.some()
// arr.every()
// 遍历 Set 与遍历数组相同
// const s = new Set(['foo', 'bar'])
// for (const item of s) {
// console.log(item)
// }
// 遍历 Map 可以配合数组结构语法,直接获取键值
// const m = new Map()
// m.set('foo', '123')
// m.set('bar', '345')
// for (const [key, value] of m) {
// console.log(key, value)
// }
// 普通对象不能被直接 for...of 遍历
// const obj = { foo: 123, bar: 456 }
// for (const item of obj) {
// console.log(item)
// }
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
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
# 二十四、 迭代器
# 维护一个数据指针 返回一个带有 next 的方法
const set = new Set(["foo", "bar", "baz"]);
const iterator = set[Symbol.iterator]();
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
while (true) {
const current = iterator.next();
if (current.done) {
break; // 迭代已经结束了,没必要继续了
}
console.log(current.value);
}
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
# 二十五、 Generator 函数 生成器
// Generator 函数
// function * foo () {
// console.log('zce')
// return 100
// }
// const result = foo()
// console.log(result.next())
function* foo() {
console.log("1111");
yield 100;
console.log("2222");
yield 200;
console.log("3333");
yield 300;
}
const generator = foo();
console.log(generator.next()); // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
console.log(generator.next()); // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
console.log(generator.next()); // 。。。
console.log(generator.next()); // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
← 异步&事件环 手写 Promise →
