Iterator

什么是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对象、以及字符串

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值