for in

用于遍历对象中的可枚举属性(含原型链中的)

因数组可看作以下标为属性的对象,字符串可以被视为类似数组的对象(字符串支持通过数值索引来访问字符串中的特定字符),所以数组、字符串也可以用 for in 遍历,但不推荐,因为存在以下缺陷:

  • 遍历顺序不确定
    for…in遍历的顺序是按照属性创建的顺序,而数组的元素顺序可能会被忽略。
  • 遍历到不需要的属性
    如果数组对象拥有自定义属性或者原型链上的属性,这些也会被遍历到。

判断对象属性是否可枚举

对象.propertyIsEnumerable("属性名")
  • 1.
let me_obj = {
  name: "朝阳",
  age: 35,
};

console.log(me_obj.propertyIsEnumerable("name")); // true
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

自定义不可枚举的对象属性

Object.defineProperty(me_obj, "hobby", {
  value: "编程",
  enumerable: false,
});

console.log(me_obj.propertyIsEnumerable("hobby")); // false
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

for in 遍历对象的属性

for (const key in me_obj) {
  console.log(key); // name, age 
}
  • 1.
  • 2.
  • 3.

hobby 被设置为了不可枚举的属性,不会被遍历

for of

用于遍历可迭代数据,如字符串、数组、类数组、Set、Map

判断数据是否可迭代

通过使用 Symbol.iterator属性来判断一个数据是否是可迭代的

function isIterable(data) {
  return typeof data[Symbol.iterator] === 'function';
}
 
// 示例使用
const array = [1, 2, 3];
const string = "hello";
const number = 123;
 
console.log(isIterable(array)); // true
console.log(isIterable(string)); // true
console.log(isIterable(number)); // false
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

for of 遍历字符串

let text = "Hello, world!";
 
for (const char of text) {
    console.log(char);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

for of 遍历数组

const array = [1, 2, 3, 4, 5];
 
for (const element of array) {
  console.log(element);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

for of 遍历类数组

// 假设有一个类数组对象
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};
 
// 使用for...of循环遍历
for (const value of arrayLike) {
  console.log(value); // 输出: a, b, c
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

for of 遍历 Set

const mySet = new Set([1, 2, 3, 4, 5]);
 
for (const value of mySet) {
  console.log(value);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

for of 遍历 Map

let myMap = new Map();
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
myMap.set('key3', 'value3');
 
for (let [key, value] of myMap) {
  console.log(key + ' = ' + value);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

for await … of

ES9新增

主要用于遍历异步可迭代对象(也可用于同步可迭代对象)

因 await 需要配合 async 一起使用,所以,使用了 for await…of 外层也需要async。

function fn1() {
    return new Promise((resolve => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }))
}
function fn2() {
    return new Promise((resolve => {
        setTimeout(() => {
            resolve(2);
        }, 3000);
    }))
}
function fn3() {
    return new Promise((resolve => {
        setTimeout(() => {
            resolve(3);
        }, 2000);
    }))
}


const arr = [fn1(), fn2(), fn3()];

!(async function () {
    for await (const value of arr) {
        console.log(value);
    }
})();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

执行结果:

// 1 秒钟后
1 
// 3 秒钟后
2 
// 2 秒钟后
3
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

for await … of 和 Promise.all 的区别

如果结果都是成功

  • for await of 会把所有的结果依次输出
  • Promise.all 输出一个成功的数组,包含所有成功的返回

如果结果存在失败

  • for await of 会把所有的结果依次输出,失败的输出Uncaught(in promise) error
  • Promise.all 输出失败的返回值