for-in和for-of详解

250 阅读4分钟

for in

for in循环会遍历对象的所有可枚举属性,包括原型链上的属性。这些属性是对象的键(数组的索引),这些键通常是字符串类型,或者是可以转化为字符串的类型。

Symbol类型的属性不会被for in遍历 Symbol的值都是唯一的,不会和其他属性冲突,Symbol设计初衷为了解决属性冲突和名称冲突的问题,使得对象的属性能够有更好的唯一性。

可以转换成字符串的类型

JavaScript 中可以转换为字符串的类型包括:

原始字符串类型 (string)
数字 (number)
布尔值 (boolean)
null 和 undefined
对象(通过 .toString() 方法)
数组(通过 .toString() 方法)
Symbol(通过 String() 转换)
BigInt(通过 String() 转换)
这些类型可以通过隐式或显式的方式转化为字符串。

for in 的用法

      const arr=[1,2,3,4,5,6,7,8,9,10];
      const str={'hello':1,'world':2,'zhang':3,'san':4};
      for(let item in str){
        console.log('for in str',item);
      }
      for(let item in arr){
        console.log('for in arr',item);
      }

for-in.png

for in遍历的是数组的索引和对象的key值。

let myObject = {
  name: "John",
  [Symbol('id')]: 123,  // Symbol属性
  [Symbol('age')]: 30   // 另一个Symbol属性
};

for (let key in myObject) {
  console.log(key);  // 不会输出Symbol('id')和Symbol('age')
}

for in不会遍历Symbol键,如果需要遍历Symbol键,可以使用Object.getOwnPropertySymbols()方法。

let myObject = {
  name: "John",
  [Symbol('id')]: 123,  // Symbol属性
  [Symbol('age')]: 30   // 另一个Symbol属性
};

// 获取所有Symbol类型的属性键
let symbols = Object.getOwnPropertySymbols(myObject);

// 遍历所有Symbol键
symbols.forEach(symbol => {
  console.log(symbol, myObject[symbol]);
});

遍历symbol.png for-in会遍历对象的所有可枚举属性,包括从原型链继承的属性,除非使用hasOwnProperty()方法来过滤掉继承的属性。

const str = {'hello': 1, 'world': 2, 'zhang': 3, 'san': 4};

for (let key in str) {
  if (str.hasOwnProperty(key)) {  // 检查是否是对象自身的属性
    console.log(key, str[key]);
  }
}

for in遍历数组存在的问题

for in遍历数组的遍历顺序可能不是按照实际数组的内部顺序。

  const arr=[1,2,3,4,5,6,7,8,9,10];
  for (let index in arr) {
    let res = index + 1;
    console.log(res);
  }

for-in遍历数组存在的问题.png

for-in遍历的是对象属性名的顺序,数组的索引是按照字符串排列的,数组的索引是数字但是会被转换成字符串,所以for-in遍历数组的顺序是按照字符串的顺序排列的,而不是按照实际的数组顺序排列的。

for of

for of 是ES6新增的语法,用来遍历可迭代对象的值序列,,例如Array、String、TypedArray、Map、Set、NodeList、arguments对象、DOM NodeList、由生成器函数生成的生成器、用户定义的可迭代对象等。

for of遍历细节

for of循环迭代一个可迭代对象时,它首先调用可迭代对象的Symbol.iterator()方法,然后使用返回的迭代器对象的next()方法获取每个值,直到迭代器对象的next()方法返回一个done属性为true的对象为止。

let arr = [1, 2, 3, 4, 5];
for (let item of arr) {
  console.log(item);
}

输出结果为:12345

for of 的用法

      const arr=[1,2,3,4,5,6,7,8,9,10];
      const str={'hello':1,'world':2,'zhang':3,'san':4};
      for(let item of arr){
        console.log('for of arr',item);
      }
      for(let item of str){
        console.log('for of str',item);
      }

for.png

for of遍历的是数组的值和对象的值,普通对象不能遍历的原因是只有实现Symbol.iterator方法的对象才是可迭代的,而普通对象没有这个属性,所以不能使用for of遍历。

实现迭代器

const str = {
  'hello': 1,
  'world': 2,
  'zhang': 3,
  'san': 4,
  
  // 实现 Symbol.iterator
  [Symbol.iterator]: function() {
    let keys = Object.keys(this);  // 获取对象的所有键
    let index = 0;  // 迭代的初始索引
    
    // 返回迭代器对象
    return {
      next: () => {
        if (index < keys.length) {
          const key = keys[index++];
          return { value: [key, this[key]], done: false };  // 返回键值对
        } else {
          return { done: true };  // 完成遍历
        }
      }
    };
  }
};

// 使用 for...of 遍历
for (let [key, value] of str) {
  console.log(key, value);
}

输出结果:
hello 1
world 2
zhang 3
san 4

总结

for-in和for-of都是遍历对象属性的,但是for-in遍历的是数组的索引和对象的key值,继承自原型链的属性值也会遍历到,for-of遍历的是数组和对象的值。 for-in遍历数组的顺序是按照数组索引转换成字符串的顺序排列的,而不是按照实际的数组顺序排列的。

for-in。and for-of.png

使用建议

只需要键不关心值可以使用for in来遍历对象的可枚举属性(键) 按顺序遍历数组或者字符串可以使用for of遍历可迭代对象(数组、字符串、Set、Map等) 对于数组,建议使用for of或for Each而不是for-in,以确保数组的顺序遍历元素。