可迭代协议仅适用于 javascript 对象,html 标签本身不支持;dom 单个节点不可迭代,nodelist/htmlcollection 需手动添加 symbol.iterator 且必须每次返回新迭代器。

直接在 HTML 中无法实现 Symbol.iterator 协议——因为 HTML 是标记语言,不执行 JavaScript 逻辑;真正起作用的是 JS 对象是否满足可迭代协议。关键判断:只有 JS 对象(包括 DOM 元素集合、自定义类实例等)能通过实现 [Symbol.iterator] 方法支持 for...of,而纯 HTML 标签本身不参与该协议。
为什么给 DOM 元素加 [Symbol.iterator] 没用
DOM 节点(如 document.querySelector('div'))是单个元素,不是可迭代对象;NodeList 或 HTMLCollection 虽然类数组,但原生不带 [Symbol.iterator](旧版浏览器或某些环境可能缺失)。常见错误现象:
-
for (const el of document.querySelectorAll('li'))在 IE 或部分 Node.js 环境中报is not iterable - 手动在
NodeList.prototype上挂[Symbol.iterator],但未确保每次返回全新迭代器对象 - 误以为给
<ul></ul>标签写Symbol.iterator属性就能让 HTML“自动遍历”——HTML 不解析或执行该属性
实操建议:
- 优先使用
Array.from(nodeList)或[...nodeList]转为数组再遍历(兼容性好) - 若需扩展原生集合,用
if (!NodeList.prototype[Symbol.iterator]) { NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; } - 不要修改
Element.prototype—— 单个元素没有意义实现迭代器
[Symbol.iterator] 必须返回全新迭代器对象
多次 for...of 同一个对象时,如果 [Symbol.iterator] 总返回同一个迭代器实例,第二次遍历会立即结束(因为内部状态已到 done: true)。这是最常被忽略的封装陷阱。
错误写法:
class Counter {
constructor(max) {
this.max = max;
this.index = 0;
}
[Symbol.iterator]() {
return this; // ❌ 错!复用自身,状态污染
}
next() {
if (this.index
<p>正确写法(闭包隔离状态):</p>
<pre class="brush:php;toolbar:false;">class Counter {
constructor(max) {
this.max = max;
}
[Symbol.iterator]() {
let index = 0; // ✅ 每次调用都新建闭包变量
return {
next: () => {
if (index
<h3>如何让自定义类像数组一样被 <code>for...of</code> 遍历</h3>
<p>核心就两点:方法名必须是 <code>[Symbol.iterator]</code>,返回值必须是带 <code>next()</code> 的对象。不依赖 <code>this.items[Symbol.iterator]()</code> 可避免暴露内部结构。</p>
<p>典型场景:封装一个 <code>List</code> 类,底层用 <code>Array</code> 存数据,但不想让用户直接访问 <code>.items</code>。</p>
-
[Symbol.iterator]必须是无参函数,不能带参数(for...of不传参) -
next()不能是箭头函数(需闭包捕获index和this.items) - 终止必须返回
{ value: undefined, done: true },不能抛错(for...of不捕获异常) - 若需支持
break或提前退出,next()内部可检查外部中断信号,但协议本身不提供中断机制
简例:
class List {
constructor(...items) {
this.items = items;
}
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index
<p>复杂点在于状态隔离和边界处理——看似只加一个方法,但一旦返回的迭代器被多个循环共享,或 <code>next()</code> 实现有副作用,行为就会不可预测。别图省事直接 <code>return this.items[Symbol.iterator]()</code>,除非你明确接受暴露底层数组的风险。</p>前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











