for...of循环的使用

来源 | https://kai666666.com/2021/06/24/for-of循环的使用/

for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。——来源MDN

基本使用

for…of的基本使用比较简单:

// 遍历数组let array = ['a', 'b', 'c'];
for (let value of array) {  console.log(value); // 分别打印 'a' 'b' 'c'}
// 遍历字符串let str = "abc";
for (let value of str) {  console.log(value); // 分别打印 'a' 'b' 'c'}
// 遍历Maplet map = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (let value of map) {  console.log(value); // 分别打印 ["a", 1] ["b", 2] ["c", 3]}
// 遍历Setlet set = new Set(['a', 'a', 'b', 'c', 'b', 'c']);
for (let value of set) {  console.log(value); // 分别打印 'a' 'b' 'c'}
// 遍历arguments(function() {  for (let argument of arguments) {    console.log(argument); // 分别打印 'a' 'b' 'c'  }})('a', 'b', 'c');

可迭代对象

for…of的语法比较简单,上面我们遍历了这么多数据,现在我们使用for…of遍历一下对象:

let object = {  a: 1,  b: 2,  c: 3,}for (let value of object) {  console.log(value); // 报错:Uncaught TypeError: object is not iterable}

结果很不幸,使用for…of遍历对象报错了。为什么报错了,报错的错误提示写的很清楚,因为object对象不是可迭代的,也就是说它不是可迭代对象。

这里遇到一个新的名词,什么是可迭代对象呢?

要成为可迭代对象, 这个对象必须实现@@iterator方法,并且该方法返回一个符合迭代器协议的对象。

这里有2个问题,第一怎么去实现一个@@iterator方法?看到@@xxx这样的方法,想都不用想就是指[Symbol.xxx]方法,这里也就是一个方法的key是[Symbol.iterator]就可以了,比如:

let object = {  a: 1,  b: 2,  c: 3,  [Symbol.iterator]: function() {}}

第二个问题什么是符合迭代器协议的对象?首先迭代器协议的对象是一个对象,这个对象有一个next方法,这个next方法每次调用有会返回一个对象,这个返回的对象又有一个done属性和一个value属性。

其中done属性表示是否完成,如果是true则表示完成,false或者不写则表示没有完成;value表示值,也就是for…of循环时每次使用的值,如果done为true时候则可以不写。举个可迭代对象的例子:

let loop10 = {  [Symbol.iterator]: function() {    let i = 0
    return {      next: function() {        return {          value: i++,          done: i > 10        }      }    }  }}
for (let value of loop10) {  console.log(value); // 分别打印 0 1 2 3 4 5 6 7 8 9}

迭代器协议的对象也可以自己调用着玩玩:

let iterator = loop10[Symbol.iterator]();iterator.next(); // 返回 {value: 0, done: false}iterator.next(); // 返回 {value: 1, done: false}iterator.next(); // 返回 {value: 2, done: false}

当然迭代器协议的对象不仅仅只能用在for-of循环中,也可以用在数组的解构上:

let arr = [...loop10]; // arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

可迭代对象与generator函数

当我们看到一个个可迭代对象的next方法,再看看一个个的{value: 0, done: false}这种符合迭代器协议的对象,这时不想跟generator没点关系都不行了,没错generator函数返回的正是可迭代对象。

我们先使用常规方法实现一下对象的for…of遍历。

let object = {  a: 1,  b: 2,  c: 3,  [Symbol.iterator]: function() {    let keys = Object.keys(this);    let i = 0
    return {      next: function() {        return {          value: keys[i++],          done: i > keys.length        }      }    }  }}for (let value of object) {  console.log(value); // 分别打印 'a' 'b' 'c'}

使用generator函数可以简化上述步骤:

let object = {  a: 1,  b: 2,  c: 3,  [Symbol.iterator]: function *() {    let keys = Object.keys(this)    for (let i = 0; i < keys.length; i++) {      yield keys[i]    }  }}for (let value of object) {  console.log(value); // 分别打印 'a' 'b' 'c'}

是不是很方便?这里偷偷告诉你一个小秘密:generator函数调用后的对象也可以用在for…of上。

let loop10Gen = function *() {  for (let i = 0; i < 10; i++) {    yield i  }}
// 注意这里是loop10Gen() 而不是loop10Genfor (const value of loop10Gen()) {  console.log(value); // 分别打印 0 1 2 3 4 5 6 7 8 9}

上面不是说了,可迭代对象要实现一个@@iterator方法,这里实现了吗?没错,这里还真实现了!你可以试试:

let itarator = loop10Gen();itarator[Symbol.iterator]() === itarator; // 返回true

于是我们就得到一个比较绕的真理:generator调用后的对象,既是可迭代对象,也是符合迭代器协议的对象。

for…of与for…in的区别

for…in遍历的是对象的可枚举属性,而for…of语句遍历的是可迭代对象所定义要迭代的数据。

由于for…in遍历的是对象的可枚举属性,所以对于数组来说打印的是键,而不是值:

let array = ['a', 'b', 'c'];
for (const value in array) {  console.log(value); // 分别打印 '0' '1' '2'}
for (const value of array) {  console.log(value); // 分别打印 'a' 'b' 'c'}

for…in会遍历对象原型和原型链上的可枚举的属性。

let array = ['a', 'b', 'c'];
Object.prototype.formObject = true;Array.prototype.formArray = true;array.hello = 'world'

for (const value in array) {  console.log(value); // 分别打印 0 1 2 hello formArray formObject}
for (const value of array) {  console.log(value); // 分别打印 'a' 'b' 'c'}

通常为了避免for…in遍历原型和原型链上无关的可枚举属性,使用Object.hasOwnProperty()方法来判断:

let array = ['a', 'b', 'c'];
Object.prototype.formObject = true;Array.prototype.formArray = true;array.hello = 'world'
for (const value in array) {  if (Object.hasOwnProperty.call(array, value)) {    console.log(value); // 分别打印 0 1 2 hello  }}
for (const value of array) {  console.log(value); // 分别打印 'a' 'b' 'c'}

可迭代对象的return方法

可迭代对象除了next方法外还有return方法,主要用在循环中断的时候会调用,比如是用break关键字、或者抛出一个Error:

let loop10 = {  [Symbol.iterator]: function() {    let i = 0
    return {      next: function() {        return {          value: i++,          done: i > 10        }      },      return: function() {        console.log('return调用了~~')        return { done: true };      }    }  }}
for (let value of loop10) {  console.log(value); // 分别打印 0 1 2 3  if (value === 3) {    break; // 打印 'return调用了~~'  }}

本文完~

学习更多技能

请点击下方公众号

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值