1. 从“遍历三代人”说起:for-of 到底解决了什么问题
写 JavaScript 写了几年,回头看看数组遍历这件事,真算得上一部演化史。最早我们写 for (var i = 0; i < arr.length; i++),条件判断、步长递增、索引取值,三件套一次写齐,繁琐但踏实。后来 ES5 带来了 forEach,写法是简洁了,可它有一个先天缺陷:break 和 continue 在里面根本不能用,想中途跳出循环?门都没有。再后来 jQuery 的 $.each、underscore 的 _.each,本质上都是回调函数的包装,終归不够“原生”。
for-of 在 ES6 里登场,就是来终结这个尴尬局面的。它既能像 for 一样用 break、continue、return 控制循环流程,写法上又比传统 for 简洁得多。但如果你以为 for-of 只是“数组遍历的语法糖”,那就太小看它了——它背后的迭代器协议,才是真正让 JavaScript 遍历能力脱胎换骨的东西。这篇文章我就从使用、原理、坑点三个层面,把 for-of 掰开揉碎讲清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先看用法:for-of 基本语法与常见数据类型遍历
2.1 基本语法长什么样
for-of 的语法非常简洁:
javascript复制for (const value of iterable) {
// 每次循环 value 就是当前元素
}
这里有两个细节值得注意。第一是 const:循环变量用 const 声明完全合法,因为每次迭代都是一个新的绑定,这跟 for 循环里 var i 是同一个变量、会相互覆盖有本质区别。当然你想在循环体内重新赋值给这个变量,那就得用 let,但实践中绝大多数场景根本不需要改它。
第二是右侧的 iterable。这个词是“可迭代对象”的意思,数组、字符串、Set、Map、arguments、DOM 的 NodeList、Generator 对象等,都满足这个条件。for-of 会自动调用这个对象的迭代器接口,逐个取出里面的值。
2.2 数组遍历:最基础的场景
数组是 for-of 最典型的应用场景,对比一下三种写法你就能直观感受到差异:
javascript复制const arr = [10, 20, 30];
// 传统 for
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// forEach
arr.forEach(item => console.log(item));
// for-of
for (const item of arr) {
console.log(item);
}
for-of 不需要索引、不需要回调函数、不需要关心 this 绑定,直接拿值。代码意图一目了然:我就是要遍历数组里的每个元素。
2.3 字符串遍历:按码元还是按字符?这是个问题
for-of 遍历字符串时,有一个隐性优势经常被忽略。直接用索引取字符串某个位置的字符,取到的是 UTF-16 码元,遇到 emoji 这类超出基本多语言平面的字符就会出问题:
javascript复制const str = 'a😀b';
console.log(str.length); // 4,注意不是 3
console.log(str[1]); // '\ud83d',半截乱码
for (const char of str) {
console.log(char); // 依次输出 'a'、'😀'、'b'
}
原因在于字符串的迭代器是按“码点”而不是按“码元”来遍历的。像 😀 这样需要两个码元表示的字符,在 for-of 里会被正确地当做一个字符处理。如果你的业务里涉及 emoji、生僻字、或者多语言文本处理,这一点能帮你省掉不少麻烦。
2.4 Set 与 Map:取值方式完全不同于数组
Set 遍历时,取到的是集合中的值本身;Map 遍历时,取到的是一个 [key, value] 数组:
javascript复制const set = new Set(['a', 'b', 'c']);
for (const item of set) {
console.log(item); // 依次输出 'a' 'b' 'c'
}
const map = new Map([['name', '张三'], ['age', 30]]);
for (const entry of map) {
console.log(entry); // 依次输出 ['name', '张三']、['age', 30]
}
// 更常用的写法:配合解构
for (const [key, value] of map) {
console.log(key, value);
}
第二种写法配合数组解构,是遍历 Map 最优雅的方式之一。你不需要像以前那样先 Array.from(map) 转成数组再遍历,直接上手就行。
2.5 arguments、NodeList 等类数组对象也能遍历
以前想把 arguments 转成真数组,得写 Array.prototype.slice.call(arguments) 或者 Array.from(arguments)。现在 for-of 可以直接遍历它:
javascript复制function sum() {
let total = 0;
for (const num of arguments) {
total += num;
}
return total;
}
sum(1, 2, 3, 4); // 10
浏览器里的 NodeList(比如 `doc
