使用 Generator 提升 matchAll 体验

在 js 中匹配所有往往都是 replace 中回调处理的。
虽然能达到目的,但其实不够优雅,而且不能中断。

一个例子

假设我们有如下数据。(临时编的,没好的例子)

1
2
3
工号: 1, 姓名: 张三
工号: 2, 姓名: 李四
工号: 3, 姓名: 王五

我们需要匹配出 工号 和 姓名 字段。
我之前的做法是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const arr = [];
const str = `
工号: 1, 姓名: 张三
工号: 2, 姓名: 李四
工号: 3, 姓名: 王五
`;

str.replace(/(\d+)[^:]+:\s*([^\n]+)/g, (m, id, name) => arr.push({ id, name }));

console.log(arr);
// [
// { id: '1', name: '张三' },
// { id: '2', name: '李四' },
// { id: '3', name: '王五' }
// ]

相信大部分人也都是这样做的,因为这样简单方便。
当然,我也见过有人 while + exec 去做,都没问题。

其实都 es6 了,这么写不是特别优雅,甚至不方便控制。
例如我匹配到前3条后,不继续匹配了,那只能 while + exec 去控制。
replace 根本做不到,他会匹配完所有才终止。

Generator 登场

Generator 生成的遍历器(迭代器),用在这种场景下非常棒。
我们来封装下 while + exec 操作。

1
2
3
4
5
6
function* matchAll(str, re) {
let res;
while ((res = re.exec(str)) !== null) {
yield res;
}
}

可以看到非常简单的封装,但实际使用效果非常好。

1
2
3
4
5
6
7
8
9
const str = `
工号: 1, 姓名: 张三
工号: 2, 姓名: 李四
工号: 3, 姓名: 王五
`;

for (let [m, id, name] of matchAll(str, /(\d+)[^:]+:\s*([^\n]+)/g)) {
console.log({ id, name });
}

写起来优雅了点,而且可以直接 break 来终止匹配。

小结

生成器还有很多妙用,例如 co 模块等。
肯定有更多好玩的等着我们去挖掘。
例如用他按行读取文本数据,可以优化传统 callback 体验。