当我尝试调用CrossUI(一个js框架)的函数来rtrim由jQuery作为grep的DOM的文本内容时,firefox和chrome将运行到busy。
我发现源代码中的这个正则表达式阻止了浏览器。
我试过/[\s\uFEFF\xA0]+$/
并且它有效。
为什么/(\s|\uFEFF|\xA0)+$/
被卡住了?他们之间的内在差异是什么?
$('body').text().replace(/(\s|\uFEFF|\xA0)+$/, "");
alert('pass');

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div>
<div> </div>
<div>a</div>
</div>
&#13;
答案 0 :(得分:3)
字符集/[aa]/
等同于/[a]/
:它是一个集合,假设每个元素只能出现一次(并且忽略额外的元素)。这一切都可以,因为每个选项只有一个字符 - 没有出现复杂的空间。
但是,如果失败,交替/(a|a)/
需要检查每个分支,以防万一,因为它不能保证一个分支对另一个分支的决定不会产生后果。交替不保证固定宽度,它不保证捕获组的不变性等。在这种情况下,是的,两个分支是相同的;但是regexp引擎没有检查这个。
因此,如果你有/[aa]+$/
并且正在检查aaaaX
,那么在匹配失败之前,由于非字符串结束,你有四个检查,每个字符一个,与/[a]+$/
(实际上与/a+$/
相同)。但是对于/(a|a)+$/
,您需要检查2 * (1 + 2 * (1 + 2 * (1 + 2)))
次检查,共计30次,因为每个分支都会被检查。对于字符串中的每个额外a
,您的时间加倍,因为引擎需要检查a
分支以及a
分支(!)以查看其中一个,奇迹般地,设法匹配。
所以将此问题应用于您的问题。如评论中所述,您在一个分支中有\xA0
,它也隐含在\s
中;因此/(\s|\uFEFF|\xA0)+$/
将在不在字符串末尾的空白序列中的每个
上加倍执行时间。 (实际的rtrim
部分,即被替换的部分,不会造成问题 - 字符串空白序列的结束没有任何延迟,作为第一个测试分支,全部{{1成功但不回溯。)
答案 1 :(得分:3)
根据ECMA 5.1 specification,\s
包括WhiteSpace和 LineTerminator ,而 WhiteSpace 包括U + FEFF和U + 00A0 in它的定义。
一个简单的测试
/^\s+$/.test("\ufeff\u00a0")
显示IE9和最近版本的Firefox(38)和Chrome(43)遵循这两个字符的规格。如果您决定放弃对旧浏览器的支持,则无需手动将这些字符添加到正则表达式中。只需使用\s
。
如果您需要在旧浏览器中支持它们,请将它们包含在角色类中:
[\s\ufeff\u00a0]
使用交替将导致ECMA 5.1兼容浏览器中的灾难性回溯。由于交替创建了一个选择点,以回溯 1 并且\s
匹配ECMA 5.1中的U + 00A0,\s
中的\xA0
和\s|\uFEFF|\xA0
呈现2有效选择匹配一个不间断空间。如果有一串连续的空格(由\s
定义),则需要检查O(2 n )个案,其中n是不间断空格的数量。上述子串。同样适用于\ufeff
,但这种角色大量出现的情况更为罕见。
相反,字符类不会为回溯创建选择点,因此可以安全地在新旧浏览器中使用。
1 从技术上讲,允许引擎将问题中的交替重写为字符类。但是,它在实践中并不常见,因为它使引擎的实现变得复杂。