for in ,for of, for await of 到底有什么区别?

193 阅读3分钟

日常工作中经常会用到前两个循环 对于for await of 在实际工作中还是比较少用到的 今天总结一下这三种遍历方式有什么区别?

for in

for...in 语句迭代一个对象的所有可枚举字符串属性(除 Symbol 以外),包括继承的可枚举属性。 for in 循环主要用于遍历对象,同时可以遍历对象上的原型属性

const demo = (object) => {
  for (const key in object) {
    if (Object.prototype.hasOwnProperty.call(object, key)) {
      const element = object[key];
      console.log(element);
    }
  }
};
demo({
  a: "1",
  b: "2",
  c: "3",
});

如果只需要遍历对象上的属性 可以使用Object.prototype.hasOwnProperty.call(object, key)判断是否是原型上的属性,目前推荐的方式是使用Object.hasOwn(object, key),语义化更明显。 或者迭代对象本身自己有的属性使用 Object.keys() 或者 Object.getOwnPropertyNames()

for of

for...of 循环主要用于遍历数组 同时也可以遍历可迭代属性例如Map Set NodeList Array,String,TypedArray等

遍历普通数组

const demo = (object) => {
    for (const element of object) {
        console.log(element);
    }
};
demo([
    1,2,3
])
输出 1 2 3

遍历 String 字符串

const demo = (object) => {
    for (const element of object) {
        console.log(element);
    }
};
demo('abcdefg')
输出: a b c d e f g

也可以遍历对象 使用Object.keys() 方法 然后再取值

一般推荐使用 for in 来遍历对象

使用for of 遍历Map对象时还可以提前解构

const all =  (object)=>{
    for (const [key,value] of object) {
        console.log(key,value);
    }
}
const iterable = new Map([
    ["a", 1],
    ["b", 2],
    ["c", 3],
  ]);
all(iterable)

1734759043441.png

遍历对象同时取值

const demo = (object) => {
    for (const element of Object.keys(object)) {
        console.log(element,object[element]);

    }
};
// demo('abcdefg')
demo({
  a: "1",
  b: "2",
  c: "3",
});

1734755243778.png 遍历Map对象 遍历Set对象同理

const demo = (object) => {
    for (const element of object) {
        console.log(element);

    }
};
const iterable = new Map([
    ["a", 1],
    ["b", 2],
    ["c", 3],
  ]);
// demo('abcdefg')
demo(iterable)

1734755431553.png

提前退出

使用break 可以提前退出

异步迭代 可用于串行执行异步任务

const promise = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)]
const all = async (object)=>{
    for (const element of object) {
        const data = await element
        console.log(data);
    }
}

all(promise)

1734757607731.png for await of

可用于迭代异步任务 for await...of 语句创建一个循环,该循环遍历异步可迭代对象以及同步可迭代对象。该语句只能在可以使用 await 的上下文中使用,包括异步函数体内以及模块中。

const promise = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)]

const allawaitof = async (object)=>{
    for await (const element of object) {
        console.log(element);
    }
}

// all(promise)
allawaitof(promise)

1734757819875.png

上述将await 写在外层 简化了for of循环迭代的过程 循环内部可以直接拿到await后的值。

同时for await of 也可以用于遍历实现了迭代器的数据结构,可用于迭代异步生成器或者实现了迭代器的异步可迭代对象。

迭代实现了迭代器的异步可迭代对象

const LIMIT = 3;

const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      next() {
        const done = i === LIMIT;
        const value = done ? undefined : i++;
        return Promise.resolve({ value, done });
      },
      return() {
        // 如果消费者在循环中提前调用“break”或“return”,则会运行到这里。
        return { done: true };
      },
    };
  },
};

(async () => {
  for await (const num of asyncIterable) {
    console.log(num);
  }
})();

迭代异步生成器

async function* asyncGenerator() {
  let i = 0;
  while (i < 3) {
    yield i++;
  }
}

(async () => {
  for await (const num of asyncGenerator()) {
    console.log(num);
  }
})();
// 0
// 1
// 2

也可以用来迭代同步可迭代对象和生成器。

应用场景

  1. 异步读取文件可读流
  2. 处理fetch axios等网络请求 分块传输网络请求流信息
  3. 接收websoket 消息 并循环处理消息事件流