各位观众老爷,今天咱们聊聊 JavaScript 里一个非常好用,又经常被忽略的循环方式:for...of
。这玩意儿就像个瑞士军刀,简单实用,专治各种“遍历恐惧症”。
啥是可迭代对象?
在深入 for...of
之前,先得搞清楚一个概念:可迭代对象(Iterable)。这名字听起来高大上,其实说白了,就是可以用 for...of
循环来遍历的东西。常见的可迭代对象包括:
- 数组 (Array): 这个不用多说,JavaScript 里最常用的数据结构之一。
- 字符串 (String): 别忘了,字符串也可以看作是字符的数组。
- Map: 一种键值对的集合,类似于对象,但键可以是任意类型。
- Set: 一种值的集合,里面的值都是唯一的。
- arguments 对象: 函数内部可以访问到的,包含了函数调用时传入的所有参数。
- NodeList: 通过
document.querySelectorAll()
等方法获取的 DOM 元素集合。 - 用户自定义的可迭代对象: 这个咱们后面会讲到,比较高级。
如果一个对象实现了 Symbol.iterator
方法,那它就是可迭代的。这个方法返回一个迭代器对象,迭代器对象有一个 next()
方法,每次调用 next()
方法都会返回一个包含 value
和 done
属性的对象。value
是当前遍历到的值,done
是一个布尔值,表示是否遍历完成。
for...of
的基本语法
for...of
循环的语法非常简单:
for (const element of iterable) {
// 在这里处理 element
}
element
: 每次循环迭代时,会被赋值为可迭代对象中的下一个值。iterable
: 要遍历的可迭代对象。
for...of
的威力:各种可迭代对象的遍历
咱们来举几个例子,看看 for...of
在不同可迭代对象上的表现:
1. 数组 (Array)
const myArray = ['apple', 'banana', 'cherry'];
for (const fruit of myArray) {
console.log(fruit); // 输出: apple, banana, cherry
}
是不是很简单? for...of
直接把数组里的每个元素都取出来了,避免了使用索引的麻烦。
2. 字符串 (String)
const myString = 'Hello';
for (const character of myString) {
console.log(character); // 输出: H, e, l, l, o
}
字符串被分解成一个个字符,方便你对字符串进行逐个字符的处理。
3. Map
const myMap = new Map();
myMap.set('name', 'Alice');
myMap.set('age', 30);
for (const [key, value] of myMap) {
console.log(`Key: ${key}, Value: ${value}`);
// 输出: Key: name, Value: Alice
// Key: age, Value: 30
}
注意,遍历 Map 时,for...of
返回的是一个包含键和值的数组。我们可以使用解构赋值 [key, value]
来方便地获取键和值。
4. Set
const mySet = new Set();
mySet.add('apple');
mySet.add('banana');
mySet.add('cherry');
for (const fruit of mySet) {
console.log(fruit); // 输出: apple, banana, cherry
}
Set 的遍历和数组类似,直接返回 Set 中的每个值。
5. arguments 对象
function myFunction() {
for (const arg of arguments) {
console.log(arg);
}
}
myFunction(1, 2, 3); // 输出: 1, 2, 3
在函数内部,arguments
对象可以让你访问到所有传入的参数,即使你没有在函数定义中明确声明这些参数。
6. NodeList
const paragraphs = document.querySelectorAll('p');
for (const paragraph of paragraphs) {
console.log(paragraph.textContent); // 输出所有段落的文本内容
}
这个例子需要在浏览器环境中运行。 document.querySelectorAll('p')
会返回一个包含所有 <p>
元素的 NodeList,然后你可以用 for...of
遍历这些元素。
for...of
vs. for...in
vs. forEach
JavaScript 里遍历数组或对象的方法有很多,for...of
只是其中一种。 咱们来比较一下 for...of
和另外两种常见的循环方式:for...in
和 forEach
。
特性 | for...of |
for...in |
forEach |
---|---|---|---|
遍历对象 | 可迭代对象 (数组, 字符串, Map, Set 等) | 对象的键 (包括原型链上的可枚举属性) | 数组 |
返回值 | 元素的值 | 键 (字符串) | undefined |
break / continue |
支持 | 支持 | 不支持 (只能使用 return 来模拟 continue ) |
可读性 | 高 | 较低 (容易出错) | 中等 |
-
for...in
: 这个循环主要是用来遍历对象的,而不是数组。它会遍历对象的所有可枚举属性的键,包括从原型链上继承来的属性。所以,用for...in
遍历数组很容易出错,因为它会把数组的索引当作字符串来处理,而且还会遍历到数组的原型属性。const myArray = ['apple', 'banana', 'cherry']; for (const index in myArray) { console.log(index); // 输出: 0, 1, 2, (以及可能的原型属性) }
-
forEach
: 这是数组提供的一个方法,专门用来遍历数组。它接受一个回调函数作为参数,每次循环迭代时,都会把数组的元素、索引和数组本身传递给回调函数。const myArray = ['apple', 'banana', 'cherry']; myArray.forEach((fruit, index, array) => { console.log(`Index: ${index}, Fruit: ${fruit}`); });
forEach
的一个缺点是,它不支持break
和continue
语句。如果你需要在循环中提前退出或跳过某些元素,forEach
就无能为力了。
总而言之,for...of
更加简洁明了,专门用来遍历可迭代对象的值,而 for...in
遍历对象的键,forEach
遍历数组,各有各的用途。
自定义可迭代对象
前面提到过,你可以创建自定义的可迭代对象。 这需要实现 Symbol.iterator
方法。 咱们来举个例子,创建一个可以生成斐波那契数列的迭代器:
class FibonacciSequence {
constructor(max) {
this.max = max;
}
[Symbol.iterator]() {
let a = 0;
let b = 1;
let n = 0;
const max = this.max;
return {
next() {
if (n > max) {
return { done: true };
}
const value = a;
a = b;
b = value + b;
n++;
return { value: value, done: false };
}
};
}
}
const fibonacci = new FibonacciSequence(10);
for (const number of fibonacci) {
console.log(number); // 输出斐波那契数列的前 11 项 (0 到 10)
}
这个例子稍微复杂一点,但是展示了如何使用 Symbol.iterator
来创建一个自定义的迭代器。
FibonacciSequence
类接受一个max
参数,表示要生成的斐波那契数列的最大项数。[Symbol.iterator]()
方法返回一个迭代器对象。- 迭代器对象的
next()
方法每次调用都会返回一个包含value
和done
属性的对象。 - 当
n
大于max
时,next()
方法返回{ done: true }
,表示迭代完成。
for...of
的一些高级用法
除了基本的遍历之外,for...of
还有一些高级用法,可以让你更加灵活地处理数据。
1. 解构赋值
在遍历 Map 或者包含多个属性的对象数组时,可以使用解构赋值来方便地获取属性值。
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 }
];
for (const { name, age } of users) {
console.log(`Name: ${name}, Age: ${age}`);
}
2. 与展开运算符结合
可以把 for...of
和展开运算符 (...
) 结合起来,把可迭代对象转换成数组。
const myString = 'Hello';
const characters = [...myString]; // characters 现在是一个数组: ['H', 'e', 'l', 'l', 'o']
3. 遍历生成器函数
生成器函数是一种特殊的函数,可以使用 yield
关键字来暂停和恢复执行。 for...of
可以用来遍历生成器函数生成的值。
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
for (const value of myGenerator()) {
console.log(value); // 输出: 1, 2, 3
}
for...of
的注意事项
for...of
只能用于可迭代对象。如果尝试用for...of
遍历一个不可迭代对象,会抛出一个错误。for...of
不会遍历对象的属性,只会遍历可迭代对象的值。 如果需要遍历对象的属性,应该使用for...in
或者Object.keys()
/Object.values()
/Object.entries()
方法。- 在
for...of
循环中可以使用break
和continue
语句来控制循环的流程。
总结
for...of
是 JavaScript 中一个强大而灵活的循环方式,它可以用来遍历各种可迭代对象。 相比于 for...in
和 forEach
,for...of
更加简洁明了,易于理解,并且支持 break
和 continue
语句。 掌握 for...of
可以让你写出更加优雅和高效的代码。
好了,今天的 for...of
讲座就到这里。 希望大家以后在写代码的时候,能多多使用 for...of
,告别“遍历恐惧症”,拥抱简洁高效的编程生活! 下次再见!