前几天遇到一个bug,在一个填email的文本框,当用户录入比较长的一段文本后(比如40位以上),页面就死掉了。检查后发现校验Email的是下面这样一段javascript代码:
function checkEmail(email)
{
if (email.length == 0 )
return true;
var validEmail = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
if (validEmail.test(email))
{
return true
}
return false
}
checkEmail("123456789012345678901234567890123456789012345abcdefghijkl");
第一反应是正则表达式写的有问题,'@'前后的 ([\.-]?\w+)* 都可能会引起效率问题。下面仔细分析一下:
1. 从输入的值来看, engine会首先匹配 \w+, 这是一个贪婪匹配,可以一直匹配到结尾;
2. 然后按优先级开始匹配 ([\.-]?\w+)*中的 [\.-]?\w+,这个时候前面的 \w+ 为了后面的匹配成功,必须要重现匹配,让出一点匹配的内容,假设先让出的是 'l',([\.-]?\w+)*匹配成功;
3. ([\.-]?\w+)* 意味着要尽量去匹配多次,再第二次对 [\.-]?\w+ 匹配,这个时候为了第二次匹配的成功,第一次匹配的 [\.-]?\w+ 要让出能满足第二次 [\.-]?\w+ 的内容,也就是它匹配到的'l',这个时候,第一次匹配的 [\.-]?\w+ 又不满足了,\w+ 又得让出来一个'k'。
4. 这样未知匹配次数的 ([\.-]?\w+)* 就形成了一个很大的循环,而在正则表达式中,每次匹配时被括号里模式匹配的东西都是要被存起来供以后使用的,大量的中间结果被缓存,最终导致IE死掉。
所以这是一条典型的因为循环尝试匹配导致效率低下的正则表达式, 表达式中两个 ([\.-]?\w+)* 都可能导致解释器的crash,在本例中不需要利用匹配的中间结果,所以解决的办法很简单,在括号加入一个冒号,不保存中间结果就是了。即将那个正则表达式改成如下:
/^\w+(?:[\.-]?\w+)*@\w+(?:[\.-]?\w+)*(\.\w{2,3})+$/
如果性能还是不能满足需求,可以考虑把这个正则表达式拆成几个小的表达式,分别进行验证。