什么是Iterator:
Iterator是一种接口,为不同的数据结构提供统一的访问机制。任何数据结构,只要部署Iterator接口,就可以完成遍历操作,也就是依次处理该数据结构中的所有成员。
Iterator的作用:
1、为各种数据结构提供统一的、简便的访问接口
2、使数据结构中的成员能够按照某种次序排列
3、主要供ES6中新增的for…of…进行遍历
Iterator遍历过程
1、创建一个指针对象,指向当前数据结构的起始位置。Iterator本质上是一个指针对象
2、第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
3、继续调用指针对象的next方法,将指针指向数据结构的下一个成员。
4、不停调用指针对象的next方法,直到它指向数据结构的结束位置
代码实现:
const arr = [1,2];
var it = makeIterator(arr);
it.next() //{value : 1 , done : false}
it.next() //{value : 2 , done : false}
it.next() //{value : undefined , done : true}
//每次调用指针对象的next方法,都会返回数据结构当前成员的信息。即返回包含value和done两个属性的对象--遍历器对象。value是当前成员的值,done属性是一个布尔值,表示遍历是否结束
function makeIterator(arr) {
var nextIndex = 0;
return {
next(){
return nextIndex < arr.length ?
{value: arr[nextIndex++] , done:false}:
{value: undefined , done:true}
}
}
}
//当然对于遍历器对象来说,done:false和value:undefined是可以省略的,因此上面的函数可以简写为:
function makeIterator(arr) {
var nextIndex = 0;
return {
next() {
return nextIndex < arr.length ?
{value: arr[nextIndex++]}:
{done: true};
}
}
}
遍历器对象模拟数据结构
Tterator只是把接口规范加在了数据结构上,所以,遍历器与所遍历的数据结构实际上是分开的,因此完全可以写出没有对应数据结构的遍历器对象,也就是说可以用遍历器对象模拟出相应的数据结构。
例:写一个可以无限循环的遍历器对象
let it = idMaker();
it.next() //1
it.next() //2
it.next() //3
function idMaker(){ //此时返回的遍历器对象自己描述了一个数据结构
var idx = 0
return {
next(){
return {value: idx++ , done: false}
}
}
}
在ES6中,数组原生具备Iterator接口,不用任何处理就可以被for…of…遍历循环,而没有iterator接口(比如说对象)的数据结构无法直接被for…of…遍历
数据结构的默认Iterator接口
ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性上,一个数据结构只要具有Symbol.iterator属性,那么这个数据结构就被认为是可遍历的。
调用Symbol.iterator()方法,就会得到当前数据结构默认的遍历器生成函数。
在ES6中,有三种数据结构原生具有Iterator接口:
1、数组
2、某些类似数组的对象
3、Set和Map
例:
const arr = [1,2,3];
let iter = arr[Symbol.iterator]();
iter.next() // {value : 1 , done : false}
iter.next() // {value : 2 , done : false}
iter.next() // {value : 3 , done : false}
iter.next() // {value : undefined , done : true}
为对象部署Iterator接口
因为对象的哪个属性先遍历,哪个属性后遍历是不能确定的,所以对象这一数据结构原生并没有Iterator接口。需要手动在对象的Symbol.itertor属性上部署遍历器生成方法:
例1:一个类部署Iterator接口的写法
class Range {
constructor(start , stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator] () {
return this
}
next() {
var value = this.value;
if(value < this.stop) {
this.value++;
return {done:false , value:value}
}else{
return {done:true , value:undefined}
}
}
}
function range(start , stop) {
return new Range(start ,stop)
}
const iter = range(1,5)[Symbol.iterator]()
console.log(iter.next()) //{done: false, value: 1}
console.log(iter.next()) //{done: false, value: 2}
console.log(iter.next()) //{done: false, value: 3}
console.log(iter.next()) //{done: false, value: 4}
console.log(iter.next()) //{done: true, value: undefined}
for(var i of range(1,5)) {
console.log(i) // 1,2,3,4
}
例2:通过遍历器实现指针结构
function Obj(value) {
this.value = value;
this.next = null
}
Obj.prototype[Symbol.iterator] = function() {
var iterator = {
next : next
}
var current = this;
function next() {
if(current){
var value = current.value;
var done = current === null;
current = current.next;
return {
done :done,
value : value
}
}else{
return {
done : true
}
}
}
return iterator
}
const obj1 = new Obj(1);
const obj2 = new Obj(2);
const obj3 = new Obj(3);
obj1.next = obj2;
obj2.next = obj3;
console.log(obj1);
for(let i of obj1){
console.log(i) // 1, 2, 3
}
感觉像是生成了一个链表结构,请了解的大佬指点
类似数组的对象部署Iterator接口
直接引用数组的Iterator接口就好了
例1:
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]
例2:
const iterator = {
0 : 'a',
1 : 'b',
2 : 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
}
for( var i of iterator) {
console.log(i) // 'a' , 'b' , 'c'
}
注意:普通对象使用该方法并没有效果
调用Iterator接口的场合
一些场合会默认调用Iterator接口(也就是Symbol.iterator方法)
解构赋值
对数组和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']
拓展运算符
拓展运算符也会调用默认的Iterator接口
例1:
const str = 'hello'
[...str] //['h','e','l','l','o']
例2:
const arr = [2,3,4];
[1,...arr,5] //[1,2,3,4,5]
上面的拓展运算符实际上就调用啦Iterator接口
只要某个数据结构部署了Iterator接口,就可以对它使用…拓展运算符,将其转换为数组
其他场合
由于数组的遍历会调用遍历器接口,因此任何数组作为参数的场合都调用了遍历器接口,比如:
for…of…
Array.from()
Map() Set()
Promise.all()
字符串的Iterator接口
字符串是一个类似数组的对象,因此也原生具有Iterator接口
var str = 'hi';
typeof str[Symbol.iterator] //'function'
var iter = str[Symbol.iterator](); //调用Symbol.iterator方法返回一个遍历器对象,在遍历器对象上调用next方法实现对字符串的遍历
iter.next(); //{value:'h',done:false}
iter.next(); //{value:'i',done:false}
iter.next(); //{value:undefined,done:true}
修改字符串原生的Symbol.iterator方法
const str = 'hi';
[...str] //['h','i']
str[Symbol.iterator] = function () { //改写字符串str的Symbol.iterator方法
return {
next(){
if(this._first) {
this._first = false;
return {value:'bye',done:false};
}else{
return { done: true}
}
},
_first:true
}
}
[...str] //['bye'] 此时使用...返回的值就变成了改写后的值
str //'hi'
for…of…循环
ES6中引入for…of…循环作为比哪里所有数据结构的统一方法。一个数据结构只要部署了Symbol.iterator属性,就被视为具有Iterator接口,就可以使用for…of…循环遍历它的成员。也可以说,for…of…循环内部调用的是数据结构的Symbol.iterator方法
for…of…使用范围:
数组、Set和Map结构、某些类似数组的对象(arguments对象、DOM NodeList对象)、Generator对象、以及字符串