symbol.iterator不能直接用于原始数值,需通过封装范围创建可迭代对象实现懒加载数值序列;可用类/工厂函数或生成器函数(function*)返回自定义迭代器,支持按需计算、链式操作与边界处理。

Symbol.iterator 本身不能直接用于原始数值(如 number 类型),因为原始值没有属性、无法挂载方法,也不具备迭代器接口。但你可以通过包装数值范围(如 start → end)创建一个可迭代对象,使其支持 for...of、展开运算符、解构等,并真正实现**按需、懒加载、不预分配数组**的数值序列处理。
用对象封装范围,返回自定义迭代器
核心思路是:定义一个类或工厂函数,接收起始、结束、步长等参数,其 [Symbol.iterator]() 方法返回一个迭代器对象(含 next()),每次调用只计算下一个值,不预先生成整个数组。
- 迭代器内部维护当前状态(如
current),next()每次递进并返回{ value, done } - 支持正向/负向步长,自动判断终止条件(
或 <code>>=) - 不占用额外内存存储所有数字,适合超大范围(如
range(0, 1e12))
示例:
function range(start, end, step = 1) {
return {
[Symbol.iterator]() {
let current = start;
const direction = step > 0 ? 1 : -1;
return {
next() {
if ((direction > 0 && current = end)) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
};
}
<p>// 使用
for (const n of range(1, 5)) {
console.log(n); // 1, 2, 3, 4, 5(按需产出,非一次性生成数组)
}</p><p>console.log([...range(0, 4, 2)]); // [0, 2, 4]
</p>
支持中断、复用与组合的进阶写法
真实场景中常需「取前 N 个」「跳过 M 个」「过滤偶数」等操作。借助生成器函数(function*)可更自然地表达惰性逻辑,且天然支持 break、return 和 yield 控制流。
- 生成器函数自动实现
Symbol.iterator,无需手动写next - 可配合
take、skip、filter等高阶函数做管道式处理(返回新迭代器) - 每个操作仍保持懒执行——只有被消费时才触发计算
示例(带 take 的链式调用):
function* range(start, end, step = 1) {
let current = start;
const inc = () => { current += step; };
while ((step > 0 && current = end)) {
yield current;
inc();
}
}
<p>function* take(iter, n) {
let count = 0;
for (const item of iter) {
if (count >= n) break;
yield item;
count++;
}
}</p><p>// 取前 3 个偶数:range(0,100) → filter → take(3)
const evensUpTo3 = take(
(function*() { for (const x of range(0, 100)) if (x % 2 === 0) yield x; })(),
3
);
console.log([...evensUpTo3]); // [0, 2, 4]
</p>
注意边界与类型安全
原始数值序列看似简单,但实际易踩坑:
-
浮点步长误差:避免用
0.1步长遍历(如range(0, 1, 0.1)),改用整数缩放后除法(for (let i = 0; i ) -
无限序列需显式终止:若省略
end,必须靠外部break或take控制,否则for...of会卡死 -
不可重复遍历:默认迭代器是一次性的;如需多次使用,每次调用
[Symbol.iterator]()应返回新迭代器实例(如 new Range(...))
不复杂但容易忽略











