JS中可迭代的数据类型

JavaScript 可迭代数据类型详解:掌握 for…of 的奥秘

在 JavaScript 中,可迭代对象是指实现了 可迭代协议 的对象,它们可以使用 for...of 循环进行遍历。理解哪些数据类型是可迭代的,对于编写高效、现代的 JavaScript 代码至关重要。

什么是可迭代对象?

可迭代对象必须实现 Symbol.iterator 方法,该方法返回一个迭代器对象。迭代器对象有一个 next() 方法,每次调用返回一个包含 valuedone 属性的对象。

const iterable = {
   {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 3) {
          return { value: step, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of iterable) {
  console.log(value); // 1, 2, 3
}

JavaScript 内置的可迭代数据类型

1. Array(数组)✅

数组是最常见的可迭代对象:

const fruits = ['🍎', '🍌', '🍊'];
for (const fruit of fruits) {
  console.log(fruit); // 🍎, 🍌, 🍊
}

2. String(字符串)✅

字符串也是可迭代的,会逐个返回字符:

const greeting = 'Hello';
for (const char of greeting) {
  console.log(char); // H, e, l, l, o
}

3. Map ✅

Map 对象在迭代时会返回 [key, value] 对:

const userMap = new Map([
  ['name', 'Alice'],
  ['age', 30]
]);

for (const [key, value] of userMap) {
  console.log(`${key}: ${value}`);
}
// 输出:
// name: Alice
// age: 30

4. Set ✅

Set 对象迭代时返回集合中的每个值:

const uniqueNumbers = new Set([1, 2, 2, 3]);
for (const num of uniqueNumbers) {
  console.log(num); // 1, 2, 3
}

5. TypedArray(类型化数组)✅

如 Int8Array, Uint8Array 等:

const intArray = new Int8Array([10, 20, 30]);
for (const num of intArray) {
  console.log(num); // 10, 20, 30
}

6. Arguments 对象 ✅

函数内部的 arguments 对象是可迭代的:

function sum() {
  let total = 0;
  for (const arg of arguments) {
    total += arg;
  }
  return total;
}

console.log(sum(1, 2, 3)); // 6

7. DOM 集合 ✅

许多 DOM 集合是可迭代的:

// 获取所有 div 元素
const divs = document.querySelectorAll('div');

// 使用 for...of 遍历
for (const div of divs) {
  console.log(div);
}

8. Generator 对象 ✅

生成器函数返回的对象是可迭代的:

function* generateSequence() {
  yield 1;
  yield 2;
  yield 3;
}

for (const value of generateSequence()) {
  console.log(value); // 1, 2, 3
}

不可迭代的数据类型

1. Object(普通对象)❌

普通对象默认不可迭代:

const person = { name: 'Alice', age: 30 };

// 会报错: person is not iterable
for (const key of person) {
  console.log(key);
}

2. WeakMap 和 WeakSet ❌

这些弱引用集合不可迭代:

const weakMap = new WeakMap();
weakMap.set({}, 'value');

// 报错: weakMap is not iterable
for (const entry of weakMap) {
  console.log(entry);
}

如何使普通对象可迭代

虽然普通对象默认不可迭代,但我们可以使其可迭代:

const person = {
  name: 'Alice',
  age: 30,
  job: 'Developer',
   {
    const keys = Object.keys(this);
    let index = 0;
    
    return {
      next: () => {
        if (index < keys.length) {
          const key = keys[index++];
          return { value: [key, this[key]], done: false };
        }
        return { done: true };
      }
    };
  }
};

for (const [key, value] of person) {
  console.log(`${key}: ${value}`);
}
// 输出:
// name: Alice
// age: 30
// job: Developer

for…of 与 for…in 的区别

特性for…offor…in
用途遍历可迭代对象的值遍历对象的可枚举属性
返回值键/属性名
原型链不遍历原型链遍历原型链上的可枚举属性
适用对象可迭代对象所有对象
数组遍历索引
顺序保持插入顺序不保证顺序(虽然通常按插入顺序)

实际应用场景

1. 遍历 Map 的键值对

const settings = new Map([
  ['theme', 'dark'],
  ['fontSize', 16],
  ['notifications', true]
]);

for (const [key, value] of settings) {
  console.log(`Setting ${key} is set to ${value}`);
}

2. 处理字符串中的字符

function countVowels(str) {
  const vowels = 'aeiouAEIOU';
  let count = 0;
  
  for (const char of str) {
    if (vowels.includes(char)) count++;
  }
  
  return count;
}

console.log(countVowels('Hello World')); // 3

3. 自定义数据结构的迭代

class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  
  * {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const range = new Range(1, 5);
for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

4. 异步迭代

ES2018 引入了异步迭代器,可以与 for await...of 一起使用:

async function fetchUrls(urls) {
  for await (const response of fetchMultiple(urls)) {
    console.log(await response.text());
  }
}

async function* fetchMultiple(urls) {
  for (const url of urls) {
    yield fetch(url);
  }
}

常见问题解答

Q: 如何判断一个对象是否可迭代?

function isIterable(obj) {
  return obj != null && typeof obj[Symbol.iterator] === 'function';
}

console.log(isIterable([])); // true
console.log(isIterable({})); // false

Q: 为什么普通对象不可迭代?

  • 历史原因:ES6 之前没有迭代协议
  • 设计选择:对象属性的迭代顺序不确定
  • 替代方案:使用 Object.keys(), Object.values(), Object.entries()

Q: 如何遍历对象的键值对?

const person = { name: 'Alice', age: 30 };

// 使用 Object.entries 转换为可迭代的键值对数组
for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

Q: for…of 可以用于 NodeList 吗?

是的,NodeList 是可迭代的:

const buttons = document.querySelectorAll('button');
for (const button of buttons) {
  button.addEventListener('click', handleClick);
}

总结

JavaScript 中可迭代的数据类型包括:

  • Array
  • String
  • Map
  • Set
  • TypedArray
  • Arguments 对象
  • DOM 集合
  • Generator 对象

不可迭代的数据类型包括:

  • 普通 Object
  • WeakMap
  • WeakSet

掌握这些可迭代类型及其使用场景,可以让你:

  1. 编写更简洁、更现代的代码
  2. 更好地处理集合数据
  3. 创建自定义的可迭代数据结构
  4. 利用迭代协议实现更高级的功能

for...of 循环是处理可迭代对象的强大工具,它提供了比传统 for 循环更简洁、更安全的语法,同时避免了 for...in 循环的一些陷阱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jinuss

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

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

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

打赏作者

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

抵扣说明:

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

余额充值