首页 飞机号购买自助下单官网内容详情

for...of 的秘密:迭代器与可迭代对象,你也能创造“可循环”的东西

2026-03-30 3 飞机号购买网站

你写代码之际,可曾碰到这般困惑:借由for...of对数组予以遍历,运行状况良好,一旦换成对象,却径直报错?此现象背后实则暗藏着JavaScript一套精妙的机制——迭代器协议。弄明白它,你不但能够令自身的对象也支持for...of,还能够撰写出更为优雅的循环代码。今日我们就从2026年的角度,彻彻底底地揭开这个“可循环”的奥秘。

可迭代对象的本质

处于ECMAScript规范里,要是一个对象达成了可迭代协议,那么此对象便能被称作可迭代对象;这个可迭代协议之中的核心要求是,对象必须具备一个以Symbol.iterator作为键的方法,而且该方法得返回一个迭代器对象。从时间上看,一直到2026年这个阶段,在原生层面能够支持该协议的数据结构,其中包含数组,还有字符串,另外有Map,也有Set,并且还涵盖函数内部的arguments对象,以及DOM操作返回的NodeList等等。

正是由于这些原生结构各自内部都达成了这个特殊方法,所以才能够直接运用for...of。当于浏览器控制台键入Array.prototype[Symbol.iterator]之际,所返回的乃是一个函数,借此便验证了数组所具备的可迭代性。然而普通对象{}却并不拥有此方法,因而直接开展遍历便会抛出类型错误。

迭代器的工作流程

for (let item of [1,2,3]) { console.log(item); } // 数组
for (let char of 'hello') { console.log(char); } // 字符串
for (let [key,val] of new Map([[1,2]])) { } // Map

迭代器,那是一个对象,这个对象,得遵循特定的接口。它,必须得包含一个next()方法。每次去调用next()的时候,都会返回一个对象,这个对象有着两个属性。其中一个属性是value,它表示的是当前获取到的值。另一个属性是done,这是个布尔值,它在这里的作用,是用于标记遍历是不是已经结束。当done的值为true的时候,那就表明迭代已经完成。

我们能够以手动的方式去模拟数组那种迭代的过程,先是借助数组的Symbol.iterator方法来获取迭代器,接着不断地去调用那next(),一直到返回的对象里done属性变为true。此设计模式,把“数据集合”跟“遍历方式”进行了解耦,从而致使不一样的数据结构,能够以统一的方式去被访问,这同样是for...of循环得以灵活运作的底层逻辑。

const arr = ['a', 'b', 'c'];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 'a', done: false }
console.log(iterator.next()); // { value: 'b', done: false }
console.log(iterator.next()); // { value: 'c', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

亲手打造可迭代对象

倘若,我们存有一个用以展现数字区间的对象,举例来说,是从1至5,要使它能够被for...of进行遍历。那么,我们仅仅需要为这个对象增添Symbol.iterator方法,并且在该方法的内部返回一个涵盖next函数的对象。此next函数,得对当前遍历所处位置予以维护,于每次进行调用之际,要去判定是否已然结束,随后返回与之相应的value以及done。

const range = {
  start: 1,
  end: 5,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};
for (let num of range) {
  console.log(num); // 1,2,3,4,5
}

在2026年的前端开发里头,这种实现方式依旧是相当实用的。比如说,当构建数据可视化工具之际,开发者常常得进行遍历自定义的数据结构,像时间轴上的节点或者图表的图层那般。经由实现迭代器协议,这些自定义结构便能毫无阻碍地融入到JavaScript的原生循环语法当中,进而提升代码的可读性以及可维护性。

语法糖背后的迭代器

const numbers = [...range]; // [1,2,3,4,5]
const [first, second, ...rest] = range; // first=1, second=2, rest=[3,4,5]

这表明,只要你所拥有的对象达成了Symbol.iterator办法,那它便可即刻享有这些语法糖所带来的便利之处。举例来说,你能够径直运用扩展运算符把自定义的可迭代对象转变成数组,或者借助解构赋值迅速获取前面的几个元素。这样的设计极大程度地提升了代码的表达能力,致使开发者能用数量更少的代码去完成更为复杂的操作。

const range = {
  start: 1,
  end: 5,
  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
};

生成器函数的简化之道

如若你发觉手动去编写那个迭代器对象会致使较为繁琐的状况出现,JavaScript给出了生成器函数当作更为简便快捷的一种解决方案,生成器函数是以在function关键字的后面添加星号这样一种方式来进行定义的,其内部借助yield关键字去返回每一个值,对于调用生成器函数所返回的恰恰正是那个迭代器对象,该迭代器对象会自动去管理next方法以及其状态。

const fibonacci = {
  *[Symbol.iterator]() {
    let a = 0, b = 1;
    while (true) {
      yield a;
      [a, b] = [b, a + b];
    }
  }
};
const fib = fibonacci[Symbol.iterator]();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
// 想取多少取多少

通过使用生成器函数对之前的范围对象实行重构,代码会显著变得极为简洁。我们只要把Symbol.iterator方法界定为生成器函数,接着于循环里头yield各个值便行。此种方式格外适宜用于处理存在复杂逻辑的遍历情形,像异步数据流、树形结构的深度优先遍历之类,已然成为2026年现代JavaScript开发里常常使用的技巧。

const specialIterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        if (i < 3) return { value: i++, done: false };
        return { done: true };
      },
      return() {
        console.log('提前终止了');
        return { done: true };
      }
    };
  }
};
for (let x of specialIterable) {
  console.log(x);
  if (x === 1) break; // 触发return
}
// 输出:0,1, 然后打印“提前终止了”

无限迭代与主动终止

const userList = {
  users: [
    { name: '张三', age: 18 },
    { name: '李四', age: 20 },
    { name: '王五', age: 22 }
  ],
  *[Symbol.iterator]() {
    for (let user of this.users) {
      yield user;
    }
  }
};
for (let user of userList) {
  console.log(user.name); // 张三 李四 王五
}

迭代器有个极为强大之特性在于其能呈现“无限”运行之状,举例而言,我们能够去创造出一个始终返回下一个整数的迭代器,又或者生成斐波那契数列,然而需留意的是,将for...of直接运用于无限迭代器会致使出现死循环,所以于实际操作当中一般需手动把控遍历之次数,抑或是结合解构赋值仅取前几个值。

迭代器对提前终止机制予以支持,若遍历被强行中断,像for...of循环里碰到break语句,或者解构赋值只是选取部分值的情况,JavaScript引擎会试着去调用迭代器对象之上的return方法,开发者能够借助这个钩子来开展必要的清理工作,比如关闭文件句柄或者取消网络请求,这一特性在资源管理以及性能优化过程中发挥着重要作用。

审视完这些内容之后,你可有生成对于for...of的全然新颖的认知?其并非是某种神秘莫测的魔法,而是一套构思妙巧的协议的彰显。此刻不妨开启你的编辑器,尝试着为你的某一个业务对象增进Symbol.iterator方法,去感受一番令普通对象变得“能够循环”的趣味。你遭遇过哪些饶有趣味的迭代器应用情形?欢迎于评论区展开分享交流,以使更多开发者从这套雅致的机制中获取益处。

for...of 的秘密:迭代器与可迭代对象,你也能创造“可循环”的东西

相关标签: # for...of # 迭代器 # 可迭代对象 # JavaScript # 生成器