前言
在筛选字串时,有不少选择提供我们使用,除了使用常见的直接比对字串的 includes 之外,还有 Regular Expression(以下简称 RegExp)及match 可以使用,他们的使用方法为何?我们到底该用谁?,今天94要来pk啦!看完这篇文章可以帮助你釐清自己比较适合用哪一个及他们之间的不同.
本文章会简单介绍includes,RegExp及match,如果想直接知道结果可以直接从match vs RegExp vs includes开始看
浏览器大不同
此範例使用 indexOf 及 RegExp 进行 PK,可以了解不同浏览器执行结果不同的事实(如果你过去很喜欢使用 RegExp 请先冷静点,并且不要在排斥使用 safari 开发了)
图片来源
这边可以测试一下自己爱用的浏览器
下图为个人电脑在 Chrome 及 Safari 使用 measurethat.net 执行后的结果,分别是左方的 Chrome(version 98.0.4758.80)及右方的 Safari(version 15.1)
可以看出虽然 test 及 includes 在 Chrome 表现的比较好,不过 match 则是 Safari 表现得比较好
RegExp
在 filter 输入[,\和(都可以把前端搞炸!!!!? but why?
原因为在正规表示式中有'['开头 就一定要有']'结尾,'('同理,也一定要有')'结果,而''则是要使用另一个''结尾或其余特殊字元表示法,在了解 RegExp 的规则后,介绍一下常见的 method 分别是 test 和 exec
[的详细说明
(的详细说明
\的详细说明
test
test:检查字串中是否有符合的部分,有的话回传 true 没有则回传 false
const str = 'hello';const str2 = 'hello2';// 寻找字串中有有lo的结果const regex = new RegExp('lo');// 寻找字串中有有Lo或lo的结果const regexAddFlags = new RegExp('LO', 'i');// 寻找字串中有有0~9的结果const regexUseSpecialChar = new RegExp(/\d/);console.log(regex.test(str));// expected output: trueconsole.log(regex.test(str2));// expected output: trueconsole.log(regexAddFlags.test(str));// expected output: trueconsole.log(regexAddFlags.test(str2));// expected output: trueconsole.log(regexUseSpecialChar.test(str));// expected output: falseconsole.log(regexUseSpecialChar.test(str2));// expected output: true
RegExp 中常见的 Flag
exec
exec:检查字串中是否有符合的部分,有的话以阵列的方式回传,没有则回传 null
const str = 'hello';const str2 = 'hello2';// 寻找字串中有有lo的结果const regex = new RegExp('lo');// 寻找字串中有有Lo或lo的结果const regexAddCaseInsensitive = new RegExp('LO', 'i');// 寻找字串中有有0~9的结果const regexUseSpecialChar = new RegExp(/\d/);console.log(regex.exec(str));// expected output: ["lo"]console.log(regex.exec(str2));// expected output: ["lo"]console.log(regexAddCaseInsensitive.exec(str));// expected output: ["lo"]console.log(regexAddCaseInsensitive.exec(str2));// expected output: ["lo"]console.log(regexUseSpecialChar.exec(str));// expected output: nullconsole.log(regexUseSpecialChar.exec(str2));// expected output: ["2"]
includes
和 RegExp 的 test 很类似,都是检查字串中是否有符合的部分,有的话回传 true 没有则回传 false,不同的是不能使用 Flag 或是正规表示的内容(如上面範例中的'/\d/').因此,想要达到 RegExp 不区分大小写的搜寻法可能要搭配 toUpperCase 或 toLowercase 才能达到,在 code 里看起来会比较啰唆一些
const str = 'hello';console.log(str.includes('lo'));// expected output: trueconsole.log(str.includes('Lo'));// expected output: falseconst strToUpperCase = 'hello'.toUpperCase();console.log(strToUpperCase.includes('lo'.toUpperCase()));// expected output: trueconsole.log(strToUpperCase.includes('LO'.toUpperCase()));// expected output: true
match
和 RegExp 的 exec 很类似,都是检查字串中是否有符合的部分,有的话以阵列的方式回传,没有则回传 null,同样可以使用正规表示的内容(如上面範例中的'/\d/')但不能使用 Flag.因此,想要达到 RegExp 不区分大小写的搜寻法可能要搭配 toUpperCase 或 toLowercase 或是使用正规法式法才能达到,在 code 里看起来会比较啰唆一些
const str = 'hello';const str2 = 'hello2';console.log(str.match('lo'));// expected output: ["lo"]console.log(str.match('Lo'));// expected output: nullconsole.log(str2.match(/\d/));// expected output: ["2"]const strToUpperCase = 'hello'.toUpperCase();console.log(strToUpperCase.match('lo'.toUpperCase()));// expected output: ["LO"]console.log(strToUpperCase.match('LO'.toUpperCase()));// expected output: ["LO"]
match vs RegExp vs includes
终于看完了他们三个的使用介绍觉得讲了那么多废话 我只想知道到底该用哪个而已的人请冷静点,接着就是最重要的 PK 环节了,不过在三个大 PK 之前,我们先拿 match 跟 RegExp 进行 PK 就好
match vs RegExp(test)
我们先来看一张 match 和 RegExp 的比较图
(ops/sec = operate per second)
图片来源
MDN告诉我们以下三点:
If you need to know if a string matches a regular expression RegExp, use RegExp.test().If you only want the first match found, you might want to use RegExp.exec() instead.If you want to obtain capture groups and the global flag is set, you need to use RegExp.exec() or String.prototype.matchAll() instead.参考资料:来自 MDN 的金玉良言
懒人包:如果只想知道是否符合条件的话就用test,如果想知道第一个及全部符合的是谁都用exec即可.
通通来 PK
看完了 match 和 RegExp 我们在把 includes 加进来一起做比较,下面有两张图,我们一张一张看
在第一张图中 RegExp 我们执行了两次分别是有加入 Flag 和没有加入的,可以在图中其实两者的表现并没有相差太多
在整理后的表现优劣排名为:includes>RegExp(没有 Flag)>RegExp(有 Flag)>match.
但是在第二张图中的排行结果竟然变成:RegExp(有 Flag)>RegExp(没有 Flag)>match>includes.
P.S. 以上结果皆是使用 JSBench.Me 测试的结果
conclusion
讲了那么多,我们确定了 match 和 RegExp 在 MDN 的建议下,我们该使用 RegExp,但 includes 和 RegExp 到底该用谁比较好?
个人的观点是,RegExp 和 includes 用谁都好,了解使用情境比较重要,在 Select 类型的搜寻可以大胆的使用 RegExp.但使用 input 提供使用者输入关键字搜寻时,请避免会造成 RegExp 错误的符号出现在画面上,可能会引导使用者在搜寻时输入,进而导致页面爆炸的悲剧发生,毕竟我们没办法控制使用者照着我们期望方式的输入.
说了那么多,若执意(明知山有虎,偏向虎山行)要使用RegExp请记得遵照以下武林秘笈
武林秘笈:
Escaping user input that is to be treated as a literal string within a regular expression—that would otherwise be mistaken for a special character—can be accomplished by simple replacement:
function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string}
参考资料:来自 MDN 的武林秘笈
// 先看只使用一个'['会出错的範例,在经过武林秘笈的调教后,功能的正常const replaceStr ='[123'.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')const regex = new RegExp(replaceStr);console.log(replaceStr)// expected output: "\[123"console.log(regex.test('[123'));// expected output: trueconsole.log(regex.test('[1223'));// expected output: false// 开始耍白烂丢一堆原本会出错的'['跟'(',也在经过武林秘笈的调教后,非常的正常const replaceStr2 ='[123([('.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')const regex2 = new RegExp(replaceStr2);console.log(replaceStr2)// expected output: "\[123\(\[\("console.log(regex2.test('[123([('));// expected output: trueconsole.log(regex2.test('[123('));// expected output: false// 以上看起来都很美好,不过实际上还是有些特殊的例子 // 例如下面的範例 其实我们不希望'a\[123'被找出来 但它还是会被找到.此外,只输入一个'\'还是会让js直接爆炸const replaceStr3 = 'a['.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // "a\["const replaceStr4 = 'a\['.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // "a\["const regex3 = new RegExp(replaceStr3);console.log(replaceStr3 === replaceStr4)// expected output: trueconsole.log(regex3.test('a[123'));// expected output: trueconsole.log(regex3.test('a\[123'));// expected output: true