学习内容:
- 是一种接口机制
- 让不支持遍历的数据结构“可遍历”
Iterator是一个统一的接口,这个接口有一定的特征要求。要实现这个接口需要return一个对象,并且在这个对象里有一个next()函数,这个next()函数里面要return一个对象,这个对象里面要有一个value和done属性,实现如下:
function makeIterator(arr) {
let nextIndex = 0
return {
next() {
return nextIndex < arr.length ? {
value : arr[nextIndex++],
done : false
} : {
value : undefined,
done : true
}
}
}
}
let it = makeIterator(['a', 'b', 'c'])
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
--------------------------------------
{value: 'a', done: false}
{value: 'b', done: false}
{value: 'c', done: false}
{value: undefined, done: true}
上面的代码形式和Grenerator一模一样。
当遍历不可遍历的对象时,会报下面的错误:
let classes = {
allClass : {
class1 : ['c11','c12','c13'],
class2 : ['c21','c22','c23'],
class3 : ['c31','c32','c33']
}
}
for(let c of classes) {
console.log(c);
}
-------------------------------------------------------------------------------------
Uncaught TypeError: Invalid attempt to iterate non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.
先来看看可遍历的结构特点,看下面的例子,看看数组里包含了一个特别的属性:Symbol(Symbol.iterator)
let arr = ['a', 'b', 'c']
console.log(arr);
----------------------------
(3) ['a', 'b', 'c']
0: "a"
1: "b"
2: "c"
length: 3
[[Prototype]]: Array(0)
at: ƒ at()
concat: ƒ concat()
constructor: ƒ Array()
copyWithin: ƒ copyWithin()
entries: ƒ entries()
every: ƒ every()
fill: ƒ fill()
filter: ƒ filter()
find: ƒ find()
findIndex: ƒ findIndex()
findLast: ƒ findLast()
findLastIndex: ƒ findLastIndex()
flat: ƒ flat()
flatMap: ƒ flatMap()
forEach: ƒ forEach()
includes: ƒ includes()
indexOf: ƒ indexOf()
join: ƒ join()
keys: ƒ keys()
lastIndexOf: ƒ lastIndexOf()
length: 0
map: ƒ map()
pop: ƒ pop()
push: ƒ push()
reduce: ƒ reduce()
reduceRight: ƒ reduceRight()
reverse: ƒ reverse()
shift: ƒ shift()
slice: ƒ slice()
some: ƒ some()
sort: ƒ sort()
splice: ƒ splice()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
unshift: ƒ unshift()
values: ƒ values()
Symbol(Symbol.iterator): ƒ values() // <<=== 注意看这里!!!!!!!!!
Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}
[[Prototype]]: Object
再看看哪些原生的数据结构具备Iterator接口,这些都可以直接用for...of去遍历:
- Array
- Map
- Set
- String
- TypedArray
- 函数的arguments对象
- NodeList对象
那么如何让一个不可遍历的结构变成可遍历的结构呢?
还是要再明确两个概念:
可迭代协议:当前的对象上面是否有一个Symbol.iterator,有就可以用for...of迭代
迭代器协议:
必须符合这样的条件:return { next( return {value, done} ) }
看改造代码的实现:
let classes = {
allClass: {
class1: ["c11", "c12", "c13"],
class2: ["c21", "c22", "c23"],
class3: ["c31", "c32", "c33"],
},
};
classes[Symbol.iterator] = function () {
let allClass = this.allClass;
let keys = Reflect.ownKeys(allClass);
let values = [];
return {
next() {
if (!values.length) {
if (keys.length) {
values = allClass[keys[0]];
keys.shift(); // 弹出,删除数组的第一个元素
}
}
return {
done: !values.length,
value: values.shift(),
}
}
}
}
for(let c of classes) {
console.log(c);
}
---------
c11
c12
c13
c21
c22
c23
c31
c32
c33
上一篇学习笔记学习笔记 JavaScript ES6 异步编程Grenerator用法_白鸽同学的博客-CSDN博客中,也是返回一个对象,对象里有next(),并且next()的返回值也有value,done,所以可以直接在iterator里直接使用Grenerator,这样就不需要自己定义next方法。
使用Grenerator实现如下:
let classes = {
allClass: {
class1: ["c11", "c12", "c13"],
class2: ["c21", "c22", "c23"],
class3: ["c31", "c32", "c33"],
},
};
classes[Symbol.iterator] = function* () {
let allClass = this.allClass;
let keys = Reflect.ownKeys(allClass);
let values = [];
while (1) {
if (!values.length) {
if (keys.length) {
values = allClass[keys[0]];
keys.shift();
yield values.shift();
} else {
return false;
}
} else {
yield values.shift();
}
}
};
for (let c of classes) {
console.log(c);
}
---------
c11
c12
c13
c21
c22
c23
c31
c32
c33