JavaScript中的for...of循环与迭代器是什么

大家好,我是一碗周,一个不想被喝(内卷)的前端。如果写的文章有幸可以得到你的青睐,万分有幸~

写在前面

ECMAScript新增的for...of循环,它可以遍历任何一个可迭代的对象,本篇文章将围绕for...of循环与迭代器展开,大致包括以下内容:

01_导读.png

for…of循环

ECMAScript新增的for...of循环语句用于遍历一个可迭代对象 ,可迭代对象包括ArrayMapSetStringTypeArrayarguments以及实现可迭代接口的任意对象

它的语法非常简单与for...in类似,如果不了解可以查看for…in语句了解。

如下代码展示了for...of的用法

// 创建可迭代对象
const arr = [10, 20, 30]
const map = new Map([["a", 1], ["b", 2], ["c", 3]])
const set = new Set(arr)

for (let _arr of arr) {
  console.log(_arr) // 10 20 30 
}

// 可以通过解耦赋值的方式直接将 map 中的结构展开
for (let [key, value] of map) {
  console.log(key, value) // a 1   b 2   c 3 
}
for (let _set of set) {
  console.log(_set) // 10 20 30 
}

for...of循环同最普通的for循环一样是可以终止的,示例代码如下:

const arr = [1, 2, 3, 4, 5, 6, 7, 8]

for (num of arr) {
  if (num === 3) {
    // 当 num 的值遍历到 3 时,跳过本次循环
    continue
  } else if (num === 6) {
    // 当 num 的值遍历到 6 时,终止本次循环
    break
  }
  console.log(num);
  /*
  1
  2
  4
  5
  */
}

for…in和for…of和forEach的区别

  • for...in语句以任意顺序迭代对象的可枚举属性

  • for...of语句遍历可迭代对象定义要迭代的数据

  • forEach中不可以使用break或者continue等关键字

可迭代接口

Iterator即迭代器,它是一种接口,为各种不同的数据结构提供了统一的访问机制,换句话说,只有任何数据结构部署了迭代接口,就可以使用统一的方式的来遍历它。

实现可迭代接口的数据结构,一般都自身实现或继承了以Symbol.iterator属性的,就属于可迭代对象。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。

一个包含next()方法的对象,才可以称为一个迭代对象。next()对象的会有返回一个对象,对象中包含两个值,如下所示:

  • value:迭代器返回的任何JavaScript值。donetrue时可省略。

  • done:一个布尔值,为false时表示迭代未停止,为true时立即停止迭代器,且可以省略value的值。

JavaScript原生提供的迭代器接口如下图所示:

默认的可迭代接口.png

实现可迭代接口

在前面我们学习了可迭代接口中的含义,现在我们来自己实现一个可迭代接口。

首先,我们直接使用for...of来遍历一个未实现迭代接口的对象:

const obj = {}

for (item of obj) {
    console.log(111);
}

这段代码会抛出TypeError异常,描述信息为obj is not iterable

现在我们为obj来实现一个迭代器,代码如下:

const obj = {
    [Symbol.iterator] () {
        return {
            next () {
                console.log('迭代器执行了');
                return {
                    value: '',
                    done: true
                }
            }
        }
    }
}

我们在next()方法中添加了一个打印,为了验证迭代器执行了,最终的运行结果为

迭代器执行了

为什么for...of循环体汇中的111没有打印呢,是因为next()方法中的done我们写死了,它的值为true,就停止了迭代器的执行,如果done不会为true则这个循环不会结束。

现在我们为下面这个对象实现一个可迭代接口,对象如下:

const TODOList = {
    play: ['篮球', '足球', '羽毛球'],
    job: ['coding'],
    study: ['JavaScript', 'HTML']
}

实现一个遍历所有TODO的可迭代接口,代码如下:

TodoList[Symbol.iterator] = function () {
    const arr = this.play.concat(this.job, this.study)
    let index = 0
    return {
        next: function () {
            return {
                value: arr[index],
                done: index++ >= arr.length,
            }
        },
    }
}

然后通过for...of进行遍历,代码和执行结果如下:

for (item of TodoList) {
    console.log(item)
    /* 代码运行结果
    篮球
    足球
    羽毛球
    coding
    JavaScript
    HTML
    */
}
// 实现迭代接口后 对象可以使用 ... 展开运算符
console.log(...TodoList)

迭代器的触发除了使用for...of语句会触发之外,在解耦赋值或者使用...展开运算符等都会触发迭代接口的执行

return()方法

迭代器除了next()方法,还存在一个return()方法,该方法会在迭代器出现异常或者遇到break的时候调用,一般用于清空或者释放资源。示例代码如下:

const TodoList = {
    play: ['篮球', '足球', '羽毛球'],
    job: ['coding'],
    study: ['JavaScript', 'HTML'],
}
TodoList[Symbol.iterator] = function () {
    let arr = this.play.concat(this.job, this.study)
    let index = 0
    return {
        next: function () {
            return {
                value: arr[index],
                done: index++ >= arr.length,
            }
        },
        return: function () {
            // 释放资源
            arr = index = null
            console.log('资源被清空了')
            return { done: true }
        },
    }
}
for (item of TodoList) {
    console.log(item)
    if (item === 'coding') {
        break
    }
}

代码的运行结果如下所示:

篮球
足球
羽毛球
coding
资源被清空了

模拟实现for…of循环

模拟实现for...of的语句实现起来比较简单,本质就是通过Symbol.iterator属性获取迭代器对象,然后使用while循环遍历一下即可实现,实现代码如下:

function forOf(obj, callback) {
    let iterable, result

    if (typeof obj[Symbol.iterator] !== 'function')
        throw new TypeError(result + ' is not iterable')
    if (typeof callback !== 'function')
        throw new TypeError('cb must be callback')

    iterable = obj[Symbol.iterator]()

    result = iterable.next()
    while (!result.done) {
        callback(result.value)
        result = iterable.next()
    }
}

测试一下我们这个forOf语句的,代码如下:

let arr = [1, 2, 3, 4]
forOf(arr, item => {
    console.log(item)
})

代码运行结果如下:

1
2
3
4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一碗周.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值