你以为 Set 查询只能先转成数组再 includes?Chrome 154 今天把这件事在迭代器层面彻底原生化了

写过前端的都踩过这个坑:拿到一个 generator 函数,想查它里面有没有某个值——通常要 Array.from(gen()).includes(x),整趟迭代直接消费掉了。如果 generator 代表的是无限序列,这个写法直接卡死。

今天 Chrome 154 和 Firefox 154 把 Iterator.prototype.includes() 原生化,在迭代器层面做查询,不 materializing 整个序列。

Before:转数组再查

function* ids() {
  yield 1;
  yield 2;
  yield 3;
}

// 以前:必须先全量展开成数组
const arr = Array.from(ids());
console.log(arr.includes(2)); // true

问题:无限 generator 无法用这个方法;大序列 materialization 内存开销大。

After:Iterator.prototype.includes()

function* ids() {
  yield 1;
  yield 2;
  yield 3;
}

// Chrome 154+ / Firefox 154+:直接在迭代器上查
const iter = ids();
console.log(iter.includes(2)); // true

includes() 从迭代器头部开始扫描,遇到匹配即返回 true,遇到末尾才返回 false。不破坏迭代器状态(迭代器在匹配点或末尾就停止了)。

对于无限迭代器,只要匹配项在前面,就能立即返回,不需要展开整个序列:

function* fibonacci() {
  let a = 0, b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

// 查前 100 个斐波那契数里有没有 89——不用展成长数组
const fib = fibonacci();
// includes 会停在找到匹配或迭代器耗尽时
console.log(fib.includes(89)); // true(89 在序列中)

落地检查清单

1. 兼容性: Chrome 154+ 稳定,Firefox 154+ 稳定,Safari 暂未实现。用 feature detection 保护:

if (typeof Iterator !== 'undefined' && 
    typeof Iterator.prototype.includes === 'function') {
  // 使用 includes
} else {
  // fallback: [...iter].includes(x) 或手动迭代
}

2. 消费行为: includes() 会推进迭代器到匹配点或末尾,之后迭代器已耗尽,不能重用。需要注意这个副作用。

3. 性能注意: 匹配项越靠前越快;最坏情况(无匹配)需要完整扫描序列。

4. 搭配其他 Iterator helpers: includes() 可与 filter()、map() 等组合,实现声明式数据处理:

const result = [1, 2, 3, 4, 5].values()
  .filter(x => x % 2 === 0)
  .includes(4);
console.log(result); // true

下一步

把代码库里 Array.from(...).includes() 或 [...iter].includes() 的模式搜出来,评估是否换成 Iterator.prototype.includes()。注意不要用在必须保留迭代器状态的重用场景。


来源:Chrome 154 Release Notes(developer.chrome.com,2026-09-22)+ Firefox 154 Release Notes + TC39 Iterator Includes Proposal(github.com/tc39/proposal-iterator-includes)

评论区

0 条评论

登录后可评论。

阿柯·前端架构 15 阅读