1.Iterator的概念
Iterator是一个统一的接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作,所以也可以叫做遍历器。
作用:
1.为各种数据结构,提供一个统一的、简便的访问接口
2.使得数据结构的成员能够按某种次序排列
3.ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费
2. Iterator 接口
2.1 Symbol.iterator属性
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
注:obj具备Symbol.iterator属性,是可遍历的
Symbol.iterator是遍历器生成函数,返回一个遍历器对象
2.2 迭代过程
- 通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
value 是当前属性的值, done 用于判断是否遍历结束,当 done 为 true 时则遍历结束
2.3 为对象添加 Iterator 接口
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 };
}
};
}
};
3. 调用 Iterator 接口的场合
1)解构赋值
对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
(2)扩展运算符
扩展运算符(...)也会调用默认的 Iterator 接口。
var str = 'hello';
[...str] // ['h','e','l','l','o']
(3)yield*
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 }
(4)其他场合
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])) - Promise.all()
- Promise.race()
4. 原生具备 Iterator 接口的数据结构
1. Array
2. Map
3. Set
4. TypedArray(类数组对象)
4.1 String
4.2 函数的 arguments 对象
4.3 DOM NodeList 对象
并不是所有类似数组的对象都具有 Iterator 接口,
所以建议使用Array.from方法Array.prototype.slice.call(obj)将其转为数组
Array的Iterator接口
JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。
var arr = ['a', 'b', 'c', 'd'];
var iterator = arr[Symbol.iterator]();
iterator.next() // {value: 'a', done: false}
iterator.next() // { value: 'b', done: false }
iterator.next() // { value: 'c', done: false }
iterator.next() // { value: 'd', done: false }
iterator.next() // { value: undefined, done: true }
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
Set 和 Map的 Iterator 接口
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
console.log(e);
}
// Gecko
// Trident
// Webkit
var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262
String的 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() // { value: undefined, done: true }
DOM NodeList对象和arguments对象的Iterator接口
// DOM NodeList对象
let paras = document.querySelectorAll("p");
for (let p of paras) {
p.classList.add("test");
}
// arguments对象
function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs('a', 'b');
// 'a'
// 'b'