Skip to content

Commit 8b46b3d

Browse files
committed
迭代器
1 parent 0e7a51a commit 8b46b3d

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
[响应式原理及实现](https://github.com/fltenwall/JavaScript_Interview_Question/blob/main/notes/响应式原理及实现.md)
1818

19-
迭代器生成器
19+
[迭代器](https://github.com/fltenwall/JavaScript_Interview_Question/blob/main/notes/迭代器.md)
2020

2121
[闭包](https://github.com/fltenwall/JavaScript_Interview_Question/blob/main/notes/闭包.md)
2222

notes/JavaScript/迭代器.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#### 什么是迭代器
2+
3+
迭代器是一种特殊的对象,它具有一些为迭代过程设计的接口。迭代器在结构上具有如下特征:
4+
5+
1. 有一个`next`方法,每次调用都会返回一个`结果对象`
6+
2. 结果对象有两个属性:`value``done``value`表示下一个将要返回的值,而`done`是一个布尔状态,表达当前迭代是否已经结束。如果迭代还未完成,则`done`的值为`false`, 反之则为`true`
7+
8+
#### 什么是可迭代对象
9+
10+
具有`Symbol.iterator`属性的对象,即为可迭代对象。在 ES6 中,所有`集合对象`,包括`数组``字符串``Set集合``Map 集合`
11+
12+
既然说到了`Symbol.iterator`属性,那它是什么呢?其实它是一个特殊的函数。我们可以直接来使用这个属性来进行迭代:
13+
14+
15+
```javascript
16+
let values = [1, 2, 3];
17+
let iterator = values[Symbol.iterator]();
18+
19+
console.log(iterator.next()) // {value: 1, done: false}
20+
console.log(iterator.next()) // {value: 2, done: false}
21+
console.log(iterator.next()) // {value: 3, done: false}
22+
console.log(iterator.next()) // {value: undefined, done: true}
23+
console.log(iterator.next()) // {value: undefined, done: true}
24+
```
25+
26+
可以看到,`Symbol.iterator`其实就是一个函数,它的返回值是一个特殊的对象,这个对象有两个属性:`value``done`。当迭代完成时,`done`的值为`true`,而其值为`undefined`。迭代完成后可以继续调用,没有次数的限制,但其返回的结果对象为`{value: undefined, done: true}`
27+
28+
#### 如何模拟实现一个迭代器
29+
30+
首先`Symbol.iterator`是一个函数,它应该接收一个可迭代对象。因此我们可以创建一个函数:
31+
32+
```javascript
33+
function createIterator(items){}
34+
```
35+
36+
其次该函数调用的结果会返回一个拥有`next`方法的函数:
37+
38+
```javascript
39+
function createIterator(items){
40+
return {
41+
next: function(){}
42+
}
43+
}
44+
```
45+
46+
而调用该方法会返回一个包含了`value``done`属性的结果对象。且该结果对象的`value``迭代`的,而`done`是表示当前迭代是否完成。因此我们需要一个能够记录当前迭代位置的指针,并且每一次的调用都去移动该指针。
47+
48+
```javascript
49+
function createIterator(items){
50+
let i = 0;
51+
return {
52+
next: function(){
53+
return i < items.length ?
54+
{value: items[i++], done: false} :
55+
{value: undefined, done: true};
56+
}
57+
}
58+
}
59+
```
60+
61+
我们来测试一下该函数:
62+
63+
```javascript
64+
let iterator = createIterator([1,2,3]);
65+
console.log(iterator.next()) // {value: 1, done: false}
66+
console.log(iterator.next()) // {value: 2, done: false}
67+
console.log(iterator.next()) // {value: 3, done: false}
68+
console.log(iterator.next()) // {value: undefined, done: true}
69+
console.log(iterator.next()) // {value: undefined, done: true}
70+
```
71+
72+
#### 判断对象是否为可迭代对象
73+
74+
既然可迭代对象是有`Symbol.iterator`属性的,那么我们便可以根据该属性来判断对象是否为可迭代对象:
75+
76+
```javascript
77+
function isIterable(object){
78+
return typeof object[Symbol.iterator] === 'function'
79+
}
80+
```
81+
82+
#### 内置迭代器
83+
84+
ES6 为集合对象提供了内置迭代器,在大多数情况下我们无须自己手动实现。
85+
86+
`entries()`, `values()`, `keys()`都会返回一个迭代器,但是它们的表现不同。但要注意虽然字符串也是可迭代对象,但是它没有这三个内置的迭代器方法,不过它可以使用`for...of`进行迭代。
87+
88+
- `entries()`,值为键值对的组合
89+
- `values()`,值为集合的值
90+
- `keys()`,值为集合的键名
91+
92+
我们可以使用`for...of`来访问迭代器。
93+
94+
#### entries迭代器
95+
96+
`entries`会返回一个数组,数组中包含两个元素,分别表示集合中每个元素的键和值。
97+
98+
由于集合对象的不同,返回值也有所不同。
99+
100+
```javascript
101+
let map = new Map();
102+
map.set('address', 'BeiJing');
103+
map.set('phone', '110');
104+
105+
for(let m of map.entries()){
106+
console.log(m)
107+
}
108+
109+
110+
//['address', 'BeiJing']
111+
//['phone', '110']
112+
113+
```
114+
115+
返回结果中第一个元素为键名,第二个元素为值。
116+
117+
```javascript
118+
let set = new Set(['a', 'b', 'c']);
119+
for(let s of set.entries()){
120+
console.log(s);
121+
}
122+
123+
//['a', 'a']
124+
//['b', 'b']
125+
//['c', 'c']
126+
```
127+
128+
Set 集合的返回结果中,第一个元素和第二个元素都是值,即将值作为了键。
129+
130+
```javascript
131+
let arr = ['a', 'b', 'c'];
132+
for(let item of arr.entries()){
133+
console.log(item)
134+
}
135+
136+
//[0, 'a']
137+
//[1, 'b']
138+
//[2, 'c']
139+
```
140+
数组集合的返回结果中,第一个元素为数字索引,第二个值为集合的值。
141+
142+
```javascript
143+
let str = 'abc';
144+
for(let s of str.entries()){
145+
console.log(s);
146+
}
147+
// TypeError: str.entries is not a function or its return value is not iterable
148+
```
149+
即字符串是没有entries迭代器方法的。
150+
151+
`values``keys`迭代器则分别是返回值和键。
152+
153+
#### 不同集合类型的默认迭代器
154+
155+
如果我们直接使用`for...of`而不指定迭代器时,不同类型的集合会默认调用不同的迭代器。`Map`的默认迭代器是`entries()`,也很容易理解,因为对于`Map`来说,键和值是映射关系。而对于`Set``数组`集合来说,默认迭代器是`values`
156+
157+
对于`Map`
158+
159+
```javascript
160+
let map = new Map();
161+
map.set('address', 'BeiJing');
162+
map.set('phone', '110');
163+
164+
for(let m of map){
165+
console.log(m)
166+
}
167+
168+
//['address', 'BeiJing']
169+
//['phone', '110']
170+
```
171+
172+
对于`Set`
173+
174+
```javascript
175+
let set = new Set(['a', 'b', 'c']);
176+
for(let s of set){
177+
console.log(s);
178+
}
179+
180+
//a
181+
//b
182+
//c
183+
```
184+
185+
对于`数组`
186+
```javascript
187+
let arr = ['a', 'b', 'c'];
188+
for(let item of arr){
189+
console.log(item)
190+
}
191+
192+
//a
193+
//b
194+
//c
195+
```
196+
197+
迭代器的更多能力应该和生成器一起使用才能够体现出来,我们将在下一篇探讨迭代器与生成器的配合。
198+

0 commit comments

Comments
 (0)