Iterator
Iterator
是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator
接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
- 为各种数据结构,提供一个统一的、简便的访问接口;
- 使得数据结构的成员能够按某种次序排列;
- ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of消费。
ES6 规定,默认的 Iterator
接口部署在数据结构的Symbol.iterator
属性上,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历”的。
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
2
3
4
5
6
7
8
9
10
11
12
原生具备 Iterator 接口的数据结构如下。
1、Array
2、Map
3、Set
4、String
5、TypedArray
6、函数的 arguments 对象
7、NodeList 对象
下面是两个通过调用Symbol.iterator方法来Iterator接口的例子。
//数组的遍历 部署 Iterator 接口 let iter = arr[Symbol.iterator]();
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
console.log(iter.next()) // { value: 'a', done: false }
console.log (iter.next()) // { value: 'b', done: false }
console.log (iter.next()) // { value: 'c', done: false }
console.log (iter.next()) // { value: undefined, done: true }
//字符串的遍历 部署 Iterator 接口 someString[Symbol.iterator]();
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对于类似数组的对象(存在数值键名和length属性),部署 Iterator
接口,有一个简便方法,就是Symbol.iterator
方法直接引用数组的 Iterator
接口。
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
[...document.querySelectorAll('div')] // 可以执行了
2
3
4
1、 调用iterator接口的场合
(1) 解构赋值
var set=[1,2,3]
let [first, ...rest] = set;
2
(2) 扩展运算符
var str = 'hello';
console.log([...str]) // ['h','e','l','l','o']
2
(3) yield*
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var 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 }
2
3
4
5
6
7
8
9
10
11
12
13
14
(4) 其他一些场合(for…of、Array.form())
JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。数组的遍历器接口只返回具有数字索引的属性。
var arr = ['a', 'b', 'c', 'd'];
arr.foo = 'hello';
//获取的是键名
for (let a in arr) {
console.log(a); // 0 1 2 3
}
//获取的是键值
for (let a of arr) {
console.log(a); // a b c d
}
2
3
4
5
6
7
8
9
10
Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair); // ['a', 1] // ['b', 2]
}
for (let [key, value] of map) {
console.log(key + ' : ' + value); // a : 1 // b : 2
}
2
3
4
5
6
7
并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from
方法将其转为数组。
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);
}
2
3
4
5
6
7
8
9
for...in循环有几个缺点。
1、数组的键名是数字,但是for...in
循环是以字符串作为键名“0”、“1”、“2”等等。
2、for...in
循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
3、某些情况下,for...in
循环会以任意顺序遍历键名。
总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。
for...of循环相比上面几种做法,有一些显著的优点:
1、有着同for...in
一样的简洁语法,但是没有for...in
那些缺点。
2、不同于forEach
方法,它可以与break、continue
和return
配合使用。
3、提供了遍历所有数据结构的统一操作接口(具有[Symbol.iterator]
属性)。
4、for...in
循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of
循环,允许遍历获得键值。