StringSearch类通过Symbol.iterator实现惰性可迭代字符串查找,返回增强型SearchIterator支持filter/take等链式方法,最终统一为ArrayIterator以兼容原生数组操作。

直接用 Symbol.iterator 封装字符串查找逻辑并支持链式迭代,核心是让自定义对象可被 for...of、扩展运算符([...obj])、Array.from() 等原生迭代协议识别,同时返回的迭代器本身也具备查找方法(如 .next() 后还能调用 .find() 或 .filter()),从而形成“链式”体验。
1. 定义一个可迭代的查找包装类
创建一个类(例如 StringSearch),接收原始字符串和可选配置(如是否忽略大小写、是否全局匹配、正则或子串模式)。该类实现 [Symbol.iterator]() 方法,返回一个符合迭代器协议的对象(含 next() 方法)。
关键点:迭代器内部不立即执行全部查找,而是按需(lazy)产出每次匹配结果(如 { index, value, input }),便于链式控制节奏。
- 每次
next()调用推进一次匹配(用RegExp.exec()或String.indexOf()循环实现) - 返回
{ value: { index, value }, done: boolean }格式,兼容标准迭代器 - 把原始查找状态(如 lastIndex、游标位置)封装在迭代器实例内,避免外部干扰
2. 在迭代器上挂载链式方法(如 .filter(), .map(), .take())
标准迭代器对象本身不可链式调用方法。解决方案是:让 [Symbol.iterator]() 返回的不是裸迭代器,而是一个「增强型迭代器对象」——它既满足迭代器协议,又自带方法。
例如:
class SearchIterator {
constructor(str, pattern, options) {
this.str = str;
this.pattern = pattern;
this.options = options;
this.index = 0;
this.regex = typeof pattern === 'string'
? new RegExp(pattern.replace(/[.*+?^${}()|[]\]/g, '\$&'), options.flags || 'g')
: new RegExp(pattern, options.flags || 'g');
}
<p>next() {
const match = this.regex.exec(this.str);
if (!match) return { value: undefined, done: true };
return {
value: { index: match.index, value: match[0], groups: match.groups },
done: false
};
}</p><p>// 链式方法:返回一个新的 SearchIterator,复用当前逻辑但叠加过滤
filter(fn) {
const filtered = [];
let iter = this[Symbol.iterator]();
for (let r of iter) {
if (fn(r.value)) filtered.push(r.value);
}
// 返回新迭代器,遍历已过滤结果(也可做成惰性版)
return new ArrayIterator(filtered);
}</p><p>// 取前 N 个结果
take(n) {
const results = [];
let i = 0;
for (let r of this) {
if (i >= n) break;
results.push(r.value);
i++;
}
return new ArrayIterator(results);
}
}</p>
注意:filter 和 take 返回的是新迭代器(如 ArrayIterator),它同样实现了 [Symbol.iterator],因此可继续链式调用。
3. 实现 ArrayIterator 支持无缝链式与原生数组操作
为统一链式体验,可定义一个轻量 ArrayIterator 类,内部持有一个数组,其 [Symbol.iterator]() 返回数组默认迭代器。这样所有链式方法最终都归一为数组驱动,同时仍保持可迭代性。
class ArrayIterator {
constructor(arr) {
this.arr = arr;
}
*[Symbol.iterator]() {
yield* this.arr;
}
toArray() { return [...this]; }
map(fn) { return new ArrayIterator(this.toArray().map(fn)); }
filter(fn) { return new ArrayIterator(this.toArray().filter(fn)); }
}
如此,你可以写出类似这样的链式表达:
const search = new StringSearch("abc123def456", /d+/g);
<p>for (const match of search.filter(m => m.value.length > 1).take(2)) {
console.log(match); // { index: 3, value: "123" }, { index: 9, value: "456" }
}</p><p>// 或转成数组处理
const nums = search.map(m => Number(m.value)).filter(n => n > 200).toArray(); // [456]</p>
4. 实际封装建议:暴露简洁 API,隐藏迭代器细节
用户不需要手动调用 [Symbol.iterator]。应提供高层方法如 .all()(返回全部匹配)、.first()、.each(cb),同时保留 for...of 兼容性。
- 构造函数接受字符串 + 模式,自动推导是否正则/子串
- 内置
caseInsensitive、fromIndex、maxCount等选项 - 所有链式方法返回新实例(immutable),不修改原始状态
- 可选支持异步迭代(
Symbol.asyncIterator)用于大文本流式处理
这样,复杂查找逻辑被封装在迭代过程里,使用者既能享受原生迭代语法的简洁,又能通过链式组合灵活裁剪结果,真正实现「声明式查找」。










