【ES6标准入门】JavaScript中的迭代艺术:Interator遍历器和for..of循环的使用和原理

本文详细介绍了JavaScript中的Iterator接口及其在遍历器和for...of循环中的应用,包括遍历器的概念、部署方式、for...of循环的工作原理以及与for循环、forEach和for...in的比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

😁 作者简介:一名大三的学生,致力学习前端开发技术
⭐️个人主页:夜宵饽饽的主页
❔ 系列专栏:前端js专栏
👐学习格言:成功不是终点,失败也并非末日,最重要的是继续前进的勇气

​🔥​前言:

这里是关于Interator遍历器的使用详情,有关一些遍历代码,希望大家可以去打断点运行,去理解。 这是我自己的学习JavaScript的笔记,希望可以帮助到大家,欢迎大家的补充和纠正

第15章 Iterator和for…of循环

15.1 Iterator(遍历器)的概念

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制,任何数据结构,只要部署Iterator接口,就可以完成遍历操作(即依次出来该数据结构的所有成员)

遍历出现的背景和原因:

JavaScript原有的表示集合的数据结构主要是数组和对象,ES6又添加了Map和Set。这样就有4中数据集合,用户还可以组合它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象,这样就需要一种统一的接口机制来处理所有不同的数据结构

Iterator的作用有3个

  1. 为各种数据结构提供一个统一的,简便的访问接口
  2. 使得数据结构的成员能够按某种次序排列
  3. ES6创造一种新的遍历命令——for…of循环

Iterator的遍历过程如下

  1. 创建一个指针对象,指向当前数据结构的起始位置,也就是说,遍历器对象本质就是一个指针对象
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置

每次调用的时候,都会返回数据结构的当前成员的信息,具体来说,就是返回一个包含value和done属性的对象

  • value:表示当前成员的值
  • done:是一个布尔值,表示遍历是否结束

下面是一个模拟next方法返回值的例子:

var it=makeIterator(['a','b'])

console.log(it.next());
console.log(it.next());
console.log(it.next());

function makeIterator(array){
    var nextIndex=0;
    return{
        next:function(){
            return nextIndex < array.length ?
            {value:array[nextIndex++],done:false}:
            {value:undefined,done:true}
        }
    }
}

使用细节:

  1. 由于Iterator只是把接口的规格加到数据结构上,所以,遍历器与所遍历的数据结构实际上是分开的,完全可以写出没有对应数据结构的遍历器对象

    //下面是一个无限运行的遍历器对象的例子
    var it=idMaker()
    
    it.next().value //'0'
    it.next().value //'1'
    it.next().value //'2'
    
    function idMaker(){
        var index=0;
        return {
            next:function(){
                return {value:index++,done:false}
            }
        }
    }
    

15.2 默认Iterator接口

15.2.1 概述

Iterator接口的目的是为所有数据结构提供一种统一的访问机制,即for…of循环。当使用for…of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口

数据结构只要部署了Iterator接口,我们就称这种数据结构为 “可遍历”的

默认的Iterator接口部署在数据结构的Symbol.iterator属性,一个数据结构只要具有Symbol.iterator属性,就可以认为是可遍历的

Symbol.iterator本身是一个表达式,返回Symbol对象的Iterator属性,这时一个预定义好的,类型为Symbol的特殊值,所以要放在方括号中

const obj={
    [Symbol.iterator]:function(){
        return {
			next:function(){
                return{
                    value:1,
                    done:true
                }
            }
        }
    }
}

上面的代码中,对象obj是可遍历的,因为其 具有Symbol.iterator属性。执行这个属性会返回一个遍历器对象,该对象的根本特征就是具有next方法

👨 一点思考:

一个对象要可遍历的话,要有Iterator接口,这个接口就是对象的Symbol.iterator属性,这个属性值是一个对象,这个对象中有next方法

原生具备Iterator接口数据结构如下:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的arguments对象
  • NodeLIst

有了遍历器接口,数据结构就可以使用for…of循环遍历,也可以使用while循环遍历

15.2.2 对象实现遍历器接口

❓ 这些原生具备Iterator接口数据结构中,为什么没有对象呢?

😄 因为对象属性的遍历先后顺序是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口就等于部署一种线性转换,不过严格地说,对象部署遍历器接口并不是很有必要

如果一定要在对象上部署这个接口呢,我们有两种方法

1. 遍历器实现指针结构的例子:

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
            current=current.next
            return{done:false,value}
        }else{
            return {done:true}
        }
    }
    return iterator
}

var one=new Obj(1)
var two=new Obj(2)
var three=new Obj(3)

one.next=two
two.next=three

for(var i of one){
    console.log(i)
}

上面的代码首先在构造函数的原型链上部署Symbol.iterator方法,调用该方法会返回遍历器对象iterator,调用该对象的next方法,在返回一个值的它是自动将内部指针移到下一个实例

👨 一点思考和理解:

  • 什么叫指针结构,储存一个内存地址这个内存地址指向另一个变量或者数据
  • 怎么来传递这个数据,是构造函数还是点运算符
  • for…of会自动去寻找对象中的Symbol.iterator属性,并且这个属性要返回一个对象,这个对象中有一个next方法,会自动一直执行,直到返回值的done:true就不会再执行,在代码中,我创建了一个next属性给对象,并且使用这个属性达到对象的链表结构

2.添加Iterator接口的例子

let obj={
    data:['hello','world'],
    [Symbol.iterator](){
        const self=this;
        let index=0;
        return{
            next(){
                if(index<self.data.length){
                    return{
                        value:self.data[index++],
                        done:false
                    }
                }else{
                    return {value:undefined,done:true}
                }
            }
        }
    }
}
15.2.3 类似数组实现遍历器接口

对于类似数组的对象(存在数值键名和length属性),部署Iterator接口有一个简便方法,即使用Symbol.iterator方法直接引用数组的Iterator接口

let iterable={
	0:'a',
    1:'b',
    2:'c',
    length:3,
    [Symbol.iterator]:Array.prototype[Symbol.iterator]
}
for(let item of iterable){
    console.log(item) //'a','b','c'
}

❗️ 注意:普通对象部署数组的Symbol.iterator方法并无效果(就是键名不是数值的,例如字母的)

15.2.4 遍历器函数的返回值

如果Symbol.iterator方法对应的不是遍历器生成函数(即返回一个遍历器对象),解释引擎就会报错

var obj={}
obj[Symbol.iterator]=()=>1
[...obj] //TypeError:[] is not a function

15.3 调用Iterator接口的场合

  1. 解构赋值 :对数组和Set结构进行解构赋值时,会默认调用Symbol.iterator方法
  2. 扩展运算符:
  3. yield*:后面跟一个可遍历的结构,它会调用该结构的遍历器接口
  4. 其他场合:由于数组的遍历会调用遍历器接口,所以任何解释数组作为参数的场合其实都调用了遍历器的接口
    1. for…of
    2. Array.from()
    3. Map(),Set(),WeakMap(),WeakMap()
    4. Prmoise.add()
    5. Prmose.race()

15.4 遍历器对象的return(),throw()

遍历器对象除了具有next方法,还可以具有return方法和throw方法。如果自己写遍历器对象生成函数,那么next方法必须部署的,return方法和throw方法则是可选择部署的

15.4.1 return

return方法的使用场合是

  • 如果for…of循环提前退出(通常因为出错,或者有break语句或continue语句)就会调用return方法
  • 如果一个对象在完成遍历前需要清理或者释放资源,就可以部署return方法
function readLinesSync(file){
    return{
		next(){
            return {done:true}
        },
        return(){
            file.close();
            return{done:true}
        }
    }
}

下面,我们让文件的遍历提前返回,这样就会触发执行return方法

for(let line of readLinesSync(fileName)){
    console.log(line)
    break;
}

❗️ 注意:return方法必须返回一个对象

15.5 for…of()循环

for…of可以使用的范围包括以下几种

  • 数组
  • Set
  • Map
  • 某些类似数组的对象(arguments对象,DOMNodeList对象)
  • Generator对象
  • 字符串

15.6 for…of()循环与其他遍历语法的比较

1 for循环

👍 优点:可以遍历

❌ 缺点:写法麻烦

2 forEach

👍 优点:相比for循环来说,写法简单多了

❌ 缺点:无法中途跳出循环,break命令或return命令都不能奏效

3.for…in

👍 优点:相比forEach来说,是可以跳出循环

❌ 缺点:

  • 数组的键名是数组,但是for…in循环是以字符串作为键名的,’0‘,’1‘等
  • for…in 循环不仅可以遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链的某一个键
  • 某些情况下,for…in循环会以任意顺序遍历键名

总之:for…in循环主要是为遍历对象设计的,不适用于数组

4. for…of

👍 优点:

  • 有着简洁的语法,并且没有for…in缺点
  • 不同于forEach方法,它可以与breack continue和rerurn配合
  • 提供遍历了所有数据结构的统一接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值