概念
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制【使用for...of
自动检索是否具备iterator接口,若没有则返回错误不可遍历uniterable
,若检索具备iterator接口,则自动调用其内部的next()函数,因为iterator接口指向调用的是next()方法/函数,且next()返回值则是一个指针对象】
工作原理
创建一个指针对象(遍历器对象),指向数据结构的起始位置。
第一次调用next方法,指针会自动指向数据结构的第一个成员
接下来不断调用next方法,指针会一直往后移,直到指向最后一个成员
每调用next方法返回的是一个包含value和done的对象,{value:当前成员的值,done:布尔型}
*value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束
*当遍历结束的时候返回的value是undefined,done是true
模拟Iterator接口
//简单构造iterator
function makeIterator(array) {
let index = 0;
//返回一个对象,用于实例化new出来的对象
return {
//返回一个指针对象
next: () => {
//注意啊,三目运算符,好东西啊,可以代替繁琐的if...else啊
return index < array.length ? {
value: array[index++],
done: false // 表示所遍历对象还未遍历完毕
} : {
value: undefined,
done: true // 表示所遍历对象遍历结束
}
}
}
}
const item = new makeIterator([1, 2]);
console.log(item.next());//Object { value: 1, done: false }
console.log(item.next());//Object { value: 2, done: false }
console.log(item.next());//Object { value: undefined, done: true }
默认接口
具有原生iterator接口的数据类型
Array
NodeList 对象
String
Map
Set
函数的 arguments 对象
TypedArray
const item1 = [1,2,3];
const divList = document.querySelectorAll('div');
const str = "abc";
const set = new Set([1,2,3]);
const map = new Map([["userName", "burningSnow"],["age",21],["gender","male"]]);
const dataType = [item1,divList,str,set,map];
function Arguments() {
for(let i of arguments) {
console.log(i);//1,2,3
}
}
for(let index in dataType) {
for (let i of dataType[index]) {
console.log(i);
}
}
Arguments(1,2,3);
Symbol.iterator属性
对于对象object来说是没有具体默认的iterator接口
的,因为考虑到对象是多变的,然而当我们想去遍历时,那该怎末办?
ES6 规定,默认的 Iterator 接口
部署在数据结构的Symbol.iterator属性
【iterator接口
指向调用的是自定义的next()方法/函数】,或者说,一个数据结构只要具有Symbol.iterator属性
,就可以认为是“可遍历的”(iterable)
。Symbol.iterator
属性本身是一个函数【相当于上述demo中的makeIterator
函数】,就是当前数据结构默认的遍历器生成函数。
想想:我们的Symbol.iterator
应该设置在哪里呢?怎末设置?
01,设置在new出来的实例本身【不建议,没有扩展性】
// 第一种方式【比较low】,Symbol.iterator属性
let obj = {
userName: "burningSnow",
age: "21",
gender: "male",
[Symbol.iterator]: () => {
let index = 0;
let attrList = Object.getOwnPropertyNames(obj);
return {
next: () => {
return index < attrList.length ? {
value: attrList[index++], done: false
} : {
value: undefined, done: true
}
}
}
}
}
for (let i of obj) {
console.log(i);//userName,age,gender
}
// 第二种方式【结合Generator构造器】
const obj = new Proxy({
'first': 'first',
'second': 'second',
'third': undefined,
}, {
get: (target, property) => target[property] === undefined ? "暂无数据" : target[property],
})
// 布置自定义Iterator接口
Reflect.set(obj, Symbol.iterator, function* () {
for (const key in this) { // this指代数据源obj,还可以这么玩,有意思
yield this[key]
}
})
console.log([...obj]) // ["first", "second", "暂无数据"]
02,设置在其隐式原型上(实例的构造函数上)【较好,不仅封装了,还可以继承】
//原型上设置
class iterator {
//类的构造函数
constructor(userName, age) {
this.userName = userName;
this.age = age;
/**
* 设置Symbol属性作为index
* 且不会被Object.getOwnPropertyNames(this)捕获到
*/
this[Symbol.for("index")] = 0;
}
//类的一般方法,只能用简写方式,不可用function(){}或则箭头函数
[Symbol.iterator]() {
return this; // 返回对象本身【必须指定】
}
//next遍历器实现函数
next() {
let attrList = Object.getOwnPropertyNames(this);
return this[Symbol.for("index")] < attrList.length ? {
value: attrList[ this[Symbol.for("index")]++],
done: false
} : {
value: undefined,
done: true
}
}
}
let myIterator = new iterator("burningSnow", 21);
console.log(myIterator);
for (let i of myIterator) {
console.log(i);
}
myIterator实例结果图
注意:
1、只要相关的数据结构部署了Iterator接口【无论是原生部署还是自定义部署】,就可以使用扩展运算符...
和for..of
遍历
2、任意一个对象的Symbol.iterator
方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。