JS replaceAll 和 matchAll 使用指南不指北

这篇文章发布于 2022年08月21日,星期日,15:27,归类于 JS API。 阅读 9617 次, 今日 20 次 没有评论

 

封面图-纸飞机

一、先说下 replaceAll

replaceAll 这个 API 是最近一两年新出来的 API,可能某些浏览器,例如360极速版之类的并不支持(虽然使用的是 Chromium 内核,但版本太老),以及部分老旧的 Android 设备。

这个 API 我可是喜欢的很,可以轻松解决以前一些比较头疼的字符串处理需求。

比方说搜索关键字的高亮匹配。

就像下图所示这样,搜索字母 x,结果本人名字有两个字的拼音与之相符:

名字高亮

咦?不对不对,不是这个,应该是叠词匹配,也就是假设我的名字叫做:张鑫鑫,则搜索“鑫”的时候应该两个字都高亮。

上面的图我P下,稍等……

字符匹配

如果只是匹配一个字符,直接使用 replace 就可以了,简单到爆炸。

name = name.replace('鑫', '<mark>鑫</mark>');

可要是希望所有的“鑫”字都高亮,replace 就从老六变成二五仔了,只能使用正则进行替换了,由于用户输入框输入的内容是动态的,所以,只能使用 new RegExp() 实现。

// 实际开发,new RegExp() 中  
name = name.replace(new RegExp('鑫', 'g'), '<mark>鑫</mark>');

然而使用RegExp使用有两大缺点:

1. 难,新人表示臣妾记不住啊,每次使用都要看看语法是什么,哪像replace方法这么通俗易懂。
2. 烦,动态输入内容不可控,要是有特殊字符,还需要转义,不然作为 RegExp 的参数会有问题。

好在现在有了 replaceAll 方法,就可以轻松替换所有匹配的字符串了,简单到爆炸+1。

name = name.replaceAll('鑫', '<mark>鑫</mark>');

二、replaceAll的语法和细节

语法

replaceAll 语法和 replace 一样,均支持字符串替换或者正则替换:

replaceAll(pattern, replacement)

但是具体细节上会有一些区别。

细节

首先,replaceAll 中的正则表达式中一定要有’g’这个全局标识符,否则会报错。

例如下面的 JS 执行就会报错:

'hello world'.replaceAll(/\s/, '');
// 会报错

Uncaught TypeError: String.prototype.replaceAll called with a non-global RegExp argument

报错提示

其次,和replace方法一样,replaceAll也是连空字符串也能匹配替换的,例如:

'张鑫旭'.replaceAll('', '_')

返回的结果是:’_张_鑫_旭_’

如下截图所示:

空字符串替换

因此,在实现搜索高亮匹配的时候,如果是空字符串,需要不执行匹配操作,否则会插入很多预期之外的 <mark> 元素。

兼容性

IE浏览器并不支持,其他浏览器均已支持,如下图所示:

replaceAll兼容性

三、再说下 matchAll

matchAll 方法也是最近三年才支持的,支持时间比 replaceAll 方法要早个一年半载。

match 方法相比,matchAll 的返回值不仅包括匹配的内容,还包括匹配的分组(也就是正则表达式中括号括起来的部分)。

下面通过一个例子展示下两者的区别。

let str = 'author\'s name is zxx';
const reg = /\s+([a-z]+)/g;

console.log(str.match(reg));
console.log(str.matchAll(reg));

两者的返回值分别是:

match 方法返回:[' name', ' is', ' zxx']
matchAll 返回:RegExpStringIterator,转为数组后值为:

[Array(2), Array(2), Array(2)]
0: (2) [' name', 'name', index: 8, input: "author's name is zxx", groups: undefined]
1: (2) [' is', 'is', index: 13, input: "author's name is zxx", groups: undefined]
2: (2) [' zxx', 'zxx', index: 16, input: "author's name is zxx", groups: undefined]
length: 3

Chrome console面板中执行后的结果示意为:

matchAll 截图示意

可以看到,match 方法忽略的分组,直接返回匹配的数组内容,而 matchAll 返回的迭代器中的数组项中就包含了分组捕获的内容,从作用上来看,有些类似于 regexp.exec() 方法(数组项的返回值也是一样的)。

不过细节上还是有区别,matchAll 的正则是一锤子买卖,reg.lastIndex 是固定不变的,没法像 regexp.exec() 一样通过修改 lastIndex 使正则表达式前进或后退。

语法和细节

语法如下:

matchAll(regexp)

其中,regexp 表示正则表达式,其中,必须带有 ‘g’ 全局标识符,否则会报错,这一点和 replaceAll 方法一致。

当没有任何匹配的时候,match 方法返回的是 nullmatchAll 则返回的是可迭代迭代器,转数组后是空数组,和匹配时候的数据类型一致,因此,在实操上,JS 执行出错概率要更小(对于新人而言)。

兼容性

直接看图,绿意盎然:

matchAll 绿意盎然

四、结语啊哈

由于 matchAll 只能和正则表达式打交道,因此,matchAll 在 JS 代码中的传播和使用率一定是小于 replaceAll 的,因为正则表达式是很多开发人员的软肋,一知半解,能避则避。

这显然不是长久之计,正则表达式一定要认真学习,反复实践,要达到完全可以自己手写复杂正则表达式的境界才可以。

有句话是怎么说来着,凡事需要对字符串处理的地方,都可以使用正则表达式,如果正则表达式不行,那就再写一个正则表达式。

好了,就说这么多吧。

最近有个紧急项目,加班多,同时在赶《CSS选择器世界》的稿子,以及周末钓鱼比较多,所以文章更新滞后了,这一周会补上。

噢啦,啊哈,记得点赞哦!

(本篇完)

分享到:


发表评论(目前没有评论)