迭代器
Iterator是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念:
1 迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
2 迭代器是用于遍历数据结构元素的指针(如数据库中的游标)。
迭代过程
迭代的过程如下:
通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置,随后通过 next 方法进行向下迭代指向下一个位置, next 方法会返回当前位置的对象,对象包含了 value 和 done 两个属性,value 是当前属性的值, done 用于判断是否遍历结束,当 done 为 true 时则遍历结束;
const items = ["zero", "one", "two"];
const it = items[Symbol.iterator]();
it.next();
>{value: "zero", done: false}
it.next();
>{value: "one", done: false}
it.next();
>{value: "two", done: false}
it.next();
>{value: undefined, done: true}
先创建一个数组,然后通过 Symbol.iterator 方法创建一个迭代器,之后不断的调用 next 方法对数组内部项进行访问,当属性 done 为 true 时访问结束。
迭代器是协议(使用它们的规则)的一部分,用于迭代。该协议的一个关键特性就是它是顺序的:迭代器一次返回一个值。这意味着如果可迭代数据结构是非线性的(例如树),迭代将会使其线性化。
创建可迭代对象 ,在ES6中,数组、Set、Map、字符串都是可迭代对象,默认情况下定义的对象(object)是不可迭代的,但是可以通过Symbol.iterator创建迭代器。
javascript
const obj = {
items: []
}
obj.items.push(1);//这样子虽然向数组添加了新元素,但是obj不可迭代
for (let x of obj) {
console.log(x) // _iterator[Symbol.iterator] is not a function
}
//接下来给obj添加一个生成器,使obj成为一个可以迭代的对象。
const obj = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
}
obj.items.push(1)
//现在可以通过for of迭代obj了。
for (let x of obj) {
console.log(x)
}
内建迭代器
数组、Set、Map都是可迭代对象,即它们内部实现了迭代器,并且提供了3种迭代器函数调用。
1、entries() 返回迭代器**:返回键值对
javascript
//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.entries()) {
console.log(v)
}
// [0, 'a'] [1, 'b'] [2, 'c']
//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.entries()) {
console.log(v)
}
// ['a', 'a'] ['b', 'b'] ['c', 'c']
//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.entries()) {
console.log(v)
}
// ['a', 'a'] ['b', 'b']
2、values() 返回迭代器**:返回键值对的value
javascript
//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.values()) {
console.log(v)
}
//'a' 'b' 'c'
//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.values()) {
console.log(v)
}
// 'a' 'b' 'c'
//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.values()) {
console.log(v)
}
// 'a' 'b'
3、keys() 返回迭代器**:返回键值对的key
javascript
//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.keys()) {
console.log(v)
}
// 0 1 2
//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.keys()) {
console.log(v)
}
// 'a' 'b' 'c'
//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.keys()) {
console.log(v)
}
// 'a' 'b'
生成器
它是函数:用来返回迭代器。2个关键点,一个是函数、一个是返回迭代器。是ES6中特有的,一个带有*(星号)的函数,同时你也需要使用到yield。
javascript
//生成器函数,ES6内部实现了迭代器功能,你要做的只是使用yield来迭代输出。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
const a = createIterator();
console.log(a.next()); //{value: 1, done: false}
console.log(a.next()); //{value: 2, done: false}
console.log(a.next()); //{value: 3, done: false}
console.log(a.next()); //{value: undefined, done: true}
生成器的yield关键字重点就是当你执行一次next(),那么只会执行一个yield后面的内容,然后语句终止运行。
yield使用限制:yield只可以在生成器函数内部使用,如果在非生成器函数内部使用,则会报错。
javascript
function *createIterator(items) {
//你应该在这里使用yield
items.map((value, key) => {
yield value //语法错误,在map的回调函数里面使用了yield
})
}
const a = createIterator([1, 2, 3]);
console.log(a.next()); //无输出
生成器函数表达式
javascript
const createIterator = function *() {
yield 1;
yield 2;
}
const a = createIterator();
console.log(a.next());
在对象中添加生成器函数
javascript
const obj = {
a: 1,
*createIterator() {
yield this.a
}
}
const a = obj.createIterator();
console.log(a.next()); //{value: 1, done: false}