使用 Symbol.match 优化正则匹配

如果想从 'uid: 123' 字符串中匹配出 123 我通常会 'uid: 123'.match(/uid: (\d+)/)[1]
但如果字符串不符合规范,正则会返回 null 导致 [1] 操作报错 Uncaught TypeError: Cannot read property '1' of null
所以我会 ('uid: null'.match(/uid: (\d+)/) || 0)[1] 来防止报错。

但这样很不优雅。

Symbol.match

先看看官网文档 Symbol.match
写的相当简陋,甚至只说了他设为 false 时,正则将视为普通字符串,就没再说啥了。

在文章末尾的浏览器兼容性中可以看到,除了IE全兼容。

优化 match 返回值

1
2
3
4
5
6
7
const re = {
[Symbol.match](str) {
return str.match(/uid: (\d+)/) || [];
}
};
console.log('uid: 123'.match(re)[1]); // 123
console.log('uid: null'.match(re)[1]); // undefined

其实不难发现,通过 Symbol.match 劫持字符串的 match 操作,字符串的 match 操作可以自定义。
这里的 re 是死的,不方便复用,所以我们优化下代码。

1
2
3
4
5
6
7
8
9
const r = (re) => {
return {
[Symbol.match](str) {
return str.match(re) || [];
}
};
};
console.log('uid: 123'.match(r(/uid: (\d+)/))[1]); // 123
console.log('uid: null'.match(r(/uid: (\d+)/))[1]); // undefined

只是在原先的正则上加个 r 函数。
看起来不是那么好看,我们继续优化一下。

1
2
3
4
const r = (re) => ({ [Symbol.match]: (str) => str.match(new RegExp(re)) || [] });
console.log('uid: 123'.match(r(/uid: (\d+)/))[1]); // 123
console.log('uid: 123'.match(r`uid: (\\d+)`)[1]); // 123
console.log('uid: null'.match(r`uid: (\\d+)`)[1]); // undefined

这里借助 模板字符串(template literals) + 标签模板(tagged template) 优化了正则体验。

小结

这里只例举了一个小小的应用场景,作为抛砖引玉,Symbol的其他属性,也有各种不同的妙用,自己去挖掘吧。