Feature Image is photoed by ???✨ on Unsplash

从一个错误导入本文,

[code lang=typescript]const regSrc = /src=\\?"([^">]+)"/g;//声明了一个可以被复用的RegExp对象
console.log(JSON.stringify([
'<img src="https://image.yukicat.net/2020/08/20200828155507667-scaled.jpg">',
'<img src="https://image.yukicat.net/2020/07/%E8%A7%92%E7%BE%8A%E7%AC%91.gif">'
]
.map((v) => regSrc.exec(v))))
/* expect:
[
[src="https://image.yukicat.net/2020/08/20200828155507667-scaled.jpg",
"https://image.yukicat.net/2020/08/20200828155507667-scaled.jpg"
],
[
src="https://image.yukicat.net/2020/07/%E8%A7%92%E7%BE%8A%E7%AC%91.gif",
"https://image.yukicat.net/2020/07/%E8%A7%92%E7%BE%8A%E7%AC%91.gif"
]
] */
/* actually:
[
[src="https://image.yukicat.net/2020/08/20200828155507667-scaled.jpg",
"https://image.yukicat.net/2020/08/20200828155507667-scaled.jpg"
],
null
] */[/code]

为什么得不到预期的结果呢?因为exec()在设置了g或y这两种标志时是“有状态的”。在console里试一试,你会发现一个RegExp对象有一个叫lastIndex的属性。每次执行exec()后,RegExp会更新这个属性,在下次exec()时从传入的字符串的lastIndex的位置开始匹配。

设置断点后可以发现,匹配完数组中的第一个元素后<img src="https://image.yukicat.net/2020/08/20200828155507667-scaled.jpg"&gt;regSrc的lastIndex属性被设置为73,随后第二个元素执行失败,lastIndex被设置为0。

解决方案

  1. 只有有g或y标志的RegExp会保存lastIndex。将g去掉就行了。
  2. 直接使用字面量而非RegExp对象。引擎每次会根据字面量生成一个新的RegExp对象,因此这可能会产生潜在的性能问题。(我猜的)

个人觉得这种设计比较反直觉...因为每次exec()都要传入一个字符串,所以有点默认传入同一个字符串得到的结果应该会是一样的。