79 阅读6分钟

# 18、Iterator和for…of循环

## 1.Iterator(遍历器)的概念

Iterator的遍历过程如下

• 1、创建一个指针对象，指向当前数据结构的起始位置
• 2、第一次调用指针对象的next方法，可以将指针指向数据结构的第一个成员
• 3、第二次调用指针对象的next方法，指针指向数据结构的第二个成员
• 4、以此类推，不断调用指针的对象的next方法，直到它指向数据结构结束的位置

``````function makeIterator(arr) {
let nextIndex = 0;
return {
next: function() {
return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} :
{value: undefined, done: true};
}
}
};

let it = makeIterator(['a', 'b']);
it.next(). // {value: 'a', done: false}
it.next() // {value: 'b', done: false}
it.next() // {value: undefined, done: true}
``````

``````function makeIterator(arr) {
let nextIndex = 0;
return {
next: function() {
return nextIndex < arr.length ? {value: arr[nextIndex++]} : {done: true};
}
}
};

let it = makeIterator(['a', 'b']);
it.next(). // {value: 'a'}
it.next() // {value: 'b'}
it.next() // {done: true}
``````

## 2.默认Iterator接口

``````const obj = {
[Symbol.iterator] : function() {
return {
next: function() {
return {
value: 1,
done: true
}
}
}
}
}
``````

1. Array
2. Map
3. Set
4. String
5. TypedArray
6. arguments对象
7. NodeList对象

``````let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.ierator]();

iter.next(); //oouput: { value: 'a', done: false }
iter.next(); //oouput: { value: 'b', done: false }
iter.next(); //oouput: { value: 'c', done: false }
iter.next(); //oouput: { value: undefined, done: true }
``````

``````class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}

[Symbol.iterator]() { return this; }

next() {
let value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}

function range(start, stop) {
return new RangeIterator(start, stop);
}

for (let value of range(0, 3)) {
console.log(value); // 0 1 2
}
``````

``````let obj = {
data: ['hello', 'world'],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if(index < self.data.length) {
return {
value: self.data[index++],
done: false
};
}
return { value: undefined, done: true }
}
}
}
}

for (let i of obj) {
console.log(i); // hello world
}
``````

``````NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
``````

``````let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};

for (let item of iterable) {
console.log(item);
}
``````

## 3.调用Iterator接口的场合

### （1）解构赋值

``````let set = new Set().add('a').add('b').add('c');

let [x, y] = set;
let [first, ...rest] = set;
``````

### （2）扩展运算符

``````let str = 'hello';
[...str];  //output: ['h', 'e', 'l', 'l', 'o']
``````

### （3）yield*

yield*后面跟的是一个可遍历的结构，它会调用该结构的遍历器接口

``````let generator = function*() {
yield 1;
yield* [2, 3, 4];
yield 5;
}

const iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
``````

### （4）其他场合

• for…of
• Array.from()
• Map(), Set(), WeakMap(), WeakSet()
• Promise.all()
• Promise.race()

## 4.字符串的Iterator接口

``````let something = 'hi';
typeof something[Symbol.iterator]
// "function"

let iterator = something[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }
iterator.next()  // { value: "i", done: false }
iterator.next()  //
``````

``````let str = new String('Hi');
[...str];

str[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false;
return { value: 'bye', done: false };
} else {
return { done: true };
}
},
_first: true
}
}

[...str] // ["bye"]
str // "hi"
``````

## 5.Iterator接口与Generator函数

Symbol.iterator方法的最简单实现，就是Generator函数

``````let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};

[...myIterable] //[1, 2, 3]

let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};

for (let x of obj) {
console.log(x)
}
``````

## 6.遍历器对象的return，throw

return方法使用的场合是，如果for。。。of循环提前退出(通常是因为出错或者有break语句)，就会调用return方法。

``````function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
}

// 情况一
for (let line of readLinesSync(fileName)) {
console.log(line);
break;
}

// 情况二
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error();
}
``````

## 7.for…of循环

### 数组

``````const arr = ['a', 'b', 'c'];
for (let v of arr) {
console.log(v); // a b c
}
``````

for…in循环读取键名，for…of循环读取键值，但是for…of循环只返回具有数字属索引的属性

``````let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
console.log(i); // "0", "1", "2", "foo"
}

for (let item in arr) {
console.log(item);  // 3 5 7
}
``````

### Set和Map结构

Set和Map结构也具有iterator接口，可以直接使用for…of循环遍历

``````let set = new Set([1, 1, 2, 3]);
for (let item of set) {
console.log(item); // 1 2 3
}

let map = new Map().set('a', 1).set('b', 3);
for (let item of map) {
console.log(item); // ['a', 1]. ['b', 3]
}
``````

### 类似数组的对象

``````let str = 'hello';

for (let s of str) {
console.log(s);
}

let paras = document.querySelectorAll('p');
for (let p of paras) {
}

function printArgs() {
for (let x of arguments) {
console.log(x);
}
}

printArgs('a', 'b');
``````

``````for (let x of 'a\uD83D\uDC0A') {
console.log(x);  // a. 🐊
}
``````

``````let arrayLike = { length: 2, 0: 'a', 1: 'b' };

for (let x of arrayLike) {
console.log(x); //报错
}

//正确
for (let x of Array.from(arrayLike)) {
console.log(x);
}
``````

### 对象

``````let es6 = {
edition: 6,
committee: 'TC39',
standard: 'ECMA'
}

for (let i in es6) {
console.log(i);
}

for (let item in es6) {
console.log(item); //error
}
``````

``````for (let key of Object.keys(obj)) {
console.log(key + ':' + obj[key]);
}
``````

``````const obj = { a:1, b:2, c:3 };
function* entries(obj) { for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(obj)) { console.log(key, value); // a 1 b 2 c 3 }
``````