改善JavaScript程序的188个建议4150Word文档下载推荐.docx
- 文档编号:6617935
- 上传时间:2023-05-07
- 格式:DOCX
- 页数:19
- 大小:29.27KB
改善JavaScript程序的188个建议4150Word文档下载推荐.docx
《改善JavaScript程序的188个建议4150Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《改善JavaScript程序的188个建议4150Word文档下载推荐.docx(19页珍藏版)》请在冰点文库上搜索。
这可以通过使用特殊转义序列(如\l、\2等)来实现。
abcbcacba"
varr=/(\w)(\w)(\w)\2\3\1\3\2\1/;
varb=r.test(s);
//验证正则表达式是否匹配该字符串
alert(b);
//true
在上面的正则表达式中,“\1”表示对第一个反向引用(\w)所匹配的字符a的引用,“\2”表示对第二个反向引用(\w)所匹配的字符b的引用,“\3”表示对第二个反向引用(\w)所匹配的字符c的引用。
3)可以在字符串对象的replace()方法中使用。
通过使用特殊字符序列$1、$2、$3等来实现。
例如,在下面的示例中将颠倒相邻字母和数字的位置。
aa11bb22c3d4e5f6"
varr=/(\w+?
)(\d+)/g;
varb=s.replace(r,"
$2$1"
);
//"
11aa22bb3c4d5e6f"
在这个示例中,正则表达式包括两个分组,第一个分组匹配任意连续的字母,第二个分组匹配任意连续的数字。
在replace()方法的第二个参数中,$1表示对正则表达式中第一个子表达式匹配文本的引用,而$2表示对正则表达式中第二个子表达式匹配文本的引用,通过颠倒$1和$2标识符的位置即可实现字符串的颠倒以替换原字符串。
建议42:
用好正则表达式静态值
正则表达式的静态属性比较特殊,有两个名字:
长名(全称)和短名(简称,以美元符号开头表示),详细说明见表2.1。
表2.1 RegExp的静态属性
长名
短名
说明
input
$_
最后用于匹配的字符串,即传递给exec()或test()方法的字符串
lastMatch
$&
最后匹配的字符
lastParen
$+
最后匹配的分组
leftContext
$`
在上次匹配之前的子字符串
multiline
$*
用于指定是否所有表达式都使用多行模式的布尔值
rightContext
$'
在上次匹配之后的子字符串
在下面的这个示例中借助正则表达式的静态属性,匹配字符串“Javascript”,不区分大小写:
Javascript,notJavascript"
varr=/(Java)Script/gi;
vara=r.exec(s);
alert(RegExp.input);
alert(RegExp.leftContext);
//空字符串,因为在第一次匹配操作时,左侧没有内容
alert(RegExp.rightContext);
notJavascript"
alert(RegExp.lastMatch);
Javascript"
alert(RegExp.lastParen);
Java"
上面示例演示了正则表达式的几个静态属性的用法。
input属性实际上存储的是被执行匹配的字符串,即整个字符串“Javascript,notJavascript”。
leftContext属性存储的是执行第一次匹配之前的子字符串,这里为空,因为在第一次匹配时文本“Javascript”左侧为空,而rightContext属性存储的是执行第一次匹配之后的子字符串,即为“,notJavascript”。
lastMatch属性包含的是第一次匹配的子字符串,即为“Javascript”。
lastParen属性包含的是第一次匹配的分组,即为“Java”。
如果模式中包含多个分组,则会显示最后一个分组所匹配的字符。
varr=/(Java)(Script)/gi;
//执行匹配操作
//返回"
Script"
,而不再是"
也可以使用短名来读取这些属性所包含的值,考虑到这些短名不符合JavaScript语法规范,因此必须使用中括号运算符来进行读取操作。
不过对于$_属性来说,由于它符合JavaScript标识符语法规范,因此可以直接使用。
例如,针对上面示例也可以这样设计:
alert(RegExp.$_);
alert(RegExp["
$`"
]);
//空字符串
"
$+"
这些属性的值都是动态的,每次执行exec()或test()方法时,所有属性值都会被重新设置。
当在下面示例中执行第一次匹配和第二次匹配时,这些静态属性值都会实时动态更新。
varr=/Scrip(t)/gi;
//第一次定义的匹配模式
//执行第一次匹配
t"
varr=/Jav(a)/gi;
//第二次定义的匹配模式
//执行第二次匹配
Script,notJavascript"
a"
通过上面的示例可以看出,RegExp对象的静态属性是公共的,对于所有正则表达式来说是可以共享的,因此这些静态属性的值也是实时变化的。
multiline属性与上面几个属性不同,它不会根据每次执行的操作进行实时更新,并且还可以控制所有正则表达式的m标志项。
a\nb\nc"
varr=/\w+$/g;
//定义匹配模式
//执行默认匹配,返回数组["
RegExp.multiline=true;
//动态设置模式为多行匹配
b"
提示:
IE和Opera浏览器不支持RegExp.multiline属性,考虑到浏览器的兼容性,不建议读者使用这种动态方式设置正则表达式的多行匹配模式。
建议43:
使用exec增强正则表达式功能
RegExp对象定义了两个用于执行模式匹配操作的方法,它们的行为与String对象的正则表达式操作方法类似。
例如,RegExp对象的exec方法与String对象的match方法相似,只不过exec是以字符串为参数的RegExp对象方法,而match方法是以正则表达式为参数的String对象方法。
在非全局模式下,它们的返回值是相同的。
在所有RegExp模式匹配方法和String模式匹配方法中,exec方法的功能最强大。
作为正则表达式的通用匹配方法,exec方法比RegExp.test()、String.search()、String.replace()和String.match()都复杂。
该方法需要一个参数,用来执行要执行操作的字符串并返回一个数组,此数组中存放的是匹配结果。
如果没有找到匹配的文本,返回值为null。
javascript"
varr=/java/g;
java"
exec方法的工作机制是这样的:
当调用方法时,先检索字符串参数,从中获取与正则表达式相匹配的文本。
如果找到了匹配的文本,就会返回一个数组;
否则,返回null。
对返回数组的元素的具体说明如下:
第0个元素,是与表达式相匹配的文本。
第1个元素,是与正则表达式的第1个子表达式相匹配的文本(如果存在)。
第2个元素,是与正则表达式的第2个子表达式相匹配的文本,依此类推。
返回数组还包含几个属性,具体说明如下:
length,该属性声明的是数组中的元素个数。
index,该属性声明的是匹配文本的第一个字符的位置。
input,该属性包含的是整个字符串。
当调用非全局模式的正则表达式对象的exec方法时,返回的数组与调用字符串对象的match方法返回的数组是完全相同的。
当执行全局匹配模式时,exec的行为就略有变化。
这时它会定义lastIndex属性,以指定下一次执行匹配时开始检索字符串的位置。
在找到了与表达式相匹配的文本之后,exec方法将把正则表达式的lastIndex属性设置为下一次匹配执行的第一个字符的位置。
也就是说,可以通过反复地调用exec方法来遍历字符串中的所有匹配文本。
当exec再也找不到匹配的文本时,将返回null,并且把属性lastIndex重置为0。
在下面的这个示例中,定义正则表达式直接量,用来匹配字符串s中每个字符。
在循环结构的条件表达式中反复执行匹配模式,并将返回结果的值是否为null作为循环条件。
当返回值为null时,说明字符串检测完毕。
然后,读取返回数组a中包含的匹配子字符串,并调用该数组的属性index和lastIndex,其中index显示当前匹配子字符串的起始位置,而lastIndex属性显示下一次匹配操作的起始位置。
//测试使用的字符串直接量
varr=/\w/g;
//匹配模式
while((a=r.exec(s))!
=null){
//循环执行匹配操作
alert(a[0]+"
\n"
+a.index
+"
+
r.lastIndex);
/*显示每次匹配操作时返回的结果数组信息*/
}
实际上通过循环结构反复调用exec方法是唯一获得全局模式的完整模式匹配信息的方法。
无论正则表达式是否为全局模式,exec方法都会将完整的细节添加到返回数组中。
字符串对象的match方法就不同,它在全局模式下返回的数组中不会包含这么多的细节信息。
建议44:
正确使用原子组
正则表达式引擎支持一种称做原子组的属性。
原子组写作(?
>
…),也称为“贪婪”子表达式,省略号表示任意正则表达式模板、非捕获组和一个特殊的扭曲。
存在于原子组中的正则表达式组中的任何回溯点都将被丢弃。
这就为HTML正则表达式的回溯问题提供了一个更好的解决办法:
如果将[\s\S]*?
序列和它后面的HTML标记一起放在一个原子组中,所需的HTML标签被发现一次,这次匹配基本上就被锁定了。
如果正则表达式的后续部分匹配失败,原子组中的量词没有记录回溯点,那么[\s\S]*?
序列就不能扩展到已匹配的范围之外。
但是,JavaScript不支持原子组,也不提供其他方法消除不必要的回溯。
不过,可以利用前瞻过程中一项鲜为人知的行为来模拟原子组:
前瞻也是原子组。
不同的是,前瞻在整个匹配过程中不消耗字符,前瞻只是检查自己包含的模板是否能在当前位置匹配。
然而,可以避开这点,在捕获组中包装一个前瞻模板,在前瞻之外向它添加一个后向引用。
(?
=(patterntomakeatomic))\1
在任何使用原子组的模式中这个结构都是可重用的。
只要记住,需要使用适当的后向引用次数,如果正则表达式包含多个捕获组。
HTML正则表达式在使用此技术后的修改如下:
/<
html>
=([\s\S]*?
<
head>
))\1(?
title>
))\2(?
\/title>
))\3(?
\/head>
))\4(?
body>
))\5
\/body>
))\6[\s\S]*?
\/html>
/
如果没有尾随的<
/html>
,那么最后一个[\s\S]*?
将扩展至字符串结束,正则表达式将立刻失败,因为没有回溯点可以返回。
正则表达式每次找到一个中间标签就退出一个前瞻,它在前瞻过程中丢弃所有回溯位置。
下一个后向引用简单地重新匹配前瞻过程中发现的字符,将它们作为实际匹配的一部分。
建议45:
警惕嵌套量词和回溯失控
嵌套量词总是需要额外的关注和小心,以确保没有掩盖回溯失控问题。
嵌套量词出现在一个自身被重复量词修饰的组中。
嵌套量词本身并不会造成性能危害,只是在尝试匹配字符串过程中,很容易不小心在内部量词和外部量词之间,产生一大堆分解文本的方法。
例如,要匹配HTML标签,使用了下面的正则表达式:
:
[^>
'
]|"
[^"
]*"
|'
[^'
]*'
)*>
这也许过于简单,因为它不能正确地处理所有情况的有效和无效标记,但在处理有效HTML片段时应该没什么问题。
与更加简单的/<
]*>
/相比,它的优势在于涵盖了出现在属性值中的>
符号。
在非捕获组中它不使用第二和第三分支,仅匹配单引号和双引号包围的属性值,除特定的引号外允许所有字符出现。
虽然遇到了嵌套量词*,但目前还没有回溯失控的危险。
在分组的每次重复过程中,由于第二和第三分支选项严格匹配一个带引号的字符串,所以潜在的回溯点数目随目标字符串长度而呈线性增长。
但是,查看非捕获组的第一分支:
],每次只匹配一个字符,效率似乎有些低。
在字符类后面加一个量词会更好些,这样每次组重复过程就可以匹配多于一个的字符了。
正则表达式可以在目标字符串的位置上发现一个匹配。
通过每次匹配多个字符,正则表达式会在成功匹配的过程中跳过许多不必要的步骤。
如果正则表达式匹配一个“<
”字符,但后面没有“>
”,则可以令匹配成功完成,回溯失控就会进入“快车道”,因为内部量词和外部量词的排列组合产生了数量巨大的分支路径(跟在非捕获组之后)用以匹配“<
”之后的文本。
正则表达式在最终放弃匹配之前必须尝试所有的排列组合。
关于嵌套量词导致回溯失控,一个更加极端的例子是,在一大串A上应用正则表达式/(A+A+)+B/。
虽然这个正则表达式写成/AA+B/更好,但为了讨论方便,设想一下两个A能够匹配同一个字符串的多少种模板。
当应用在一个由10个A组成的字符串上(“AAAAAAAAAA”)时,正则表达式首先使用第一个A+匹配所有10个字符,然后正则表达式回溯一个字符,让第二个A+匹配最后一个字符。
这个分组试图重复,但没有更多的A,而且分组中的+量词已经符合匹配所需的最少一次,因此正则表达式开始查找B。
虽然正则表达式没有找到B,但是还不能放弃,因为还有许多路径没有被测试过。
如果第一个A+匹配8个字符,第二个A+匹配2个字符会怎么样呢?
或者第一个匹配3个,第二个匹配2个,分组重复两次,又会怎么样呢?
如果在分组的第一遍重复中,第一个A+匹配2个字符,第二个A+匹配3个字符,然后在第二遍重复中,第一个匹配1个,第二个匹配4个,又怎么样呢?
正则表达式在最坏情况下的复杂性是惊人的O(2n),也就是2的n次方。
n表示字符串的长度。
在由10个A构成的字符串中,正则表达式需要1024次回溯才能确定匹配失败,如果是20个A,回溯的数字剧增到一百万以上。
25个A足以挂起Chrome、IE、Firefox和Opera浏览器至少10分钟(如果还没死机)用以处理超过34000000次回溯以排除正则表达式的各种排列组合。
唯一的例外是最新的Safari浏览器,它能够检测到正则表达式陷入了循环,并快速终止匹配(Safari浏览器还限定了回溯的次数,超出则终止匹配尝试)。
预防此类问题的关键是确保正则表达式的两个部分不能对字符串的同一部分进行匹配。
这个正则表达式可重写为/AA+B/,但复杂的正则表达式可能难以避免此类问题。
虽然还有其他解决办法,但是增加一个模拟原子组往往作为最后一招使用,如果可能,尽可能保持正则表达式简单易懂。
如果这么做,此正则表达式将改成/((?
=(A+A+))\2)+B/,就可以彻底消除回溯问题。
建议46:
提高正则表达式执行效率
(1)关注如何让匹配更快失败
正则表达式处理慢往往是因为匹配失败过程慢,而不是匹配成功过程慢。
使用正则表达式匹配一个很大字符串的一小部分,情况更为严重,正则表达式匹配失败的位置比匹配成功的位置要多得多。
一个修改使正则表达式匹配更快但失败更慢,例如,通过增加所需的回溯次数尝试所有分支的排列组合,这通常是一个失败的修改。
(2)正则表达式以简单的、必需的字元开始
最理想的情况是,一个正则表达式的起始字元应当尽可能快速地测试并排除明显不匹配的位置。
用于此目的好的起始字元通常是一个锚(^或$)、特定字符(如x或\u363A)、字符类(如[a-z]或速记符、单词边界(\b))。
如果可能,避免以分组或选择字元开头,避免顶级分支,如/one|two/,因为这样会强迫正则表达式识别多种起始字元。
Firefox浏览器对起始字元中使用的任何量词都很敏感,能够优化得更好。
例如,以\s\s*替代\s+或\s{1,}。
其他浏览器大多优化掉这些差异。
(3)编写量词模板,使它们后面的字元互相排斥
当字符与字元相邻或子表达式能够重叠匹配时,一个正则表达式尝试分解文本的路径数量将增加。
为避免出现此现象,尽量具体化模板。
当表达“[^"
\r\n]*”时不要使用“.*?
”(依赖回溯)。
(4)减少分支的数量,缩小它们的范围
当分支使用|(竖线)时,可能要求在字符串的每一个位置上测试所有的分支选项。
通常可通过使用字符类和选项组件减少对分支的需求,或者将分支在正则表达式上的位置推后(允许到达分支之前的一些匹配尝试失败)。
字符类比分支更快,因为它们使用位向量实现(或其他快速实现)而不是回溯。
当分支必不可少时,在不影响正则表达式匹配的情况下,将常用分支放在最前面。
分支选项从左向右依次尝试,一个选项被匹配上的机会越多,它被检测的速度就越快。
注意:
由于Chrome和Firefox浏览器自动执行这些优化中的某些项目,因此较少受到手工调整的影响。
(5)使用非捕获组
捕获组花费时间和内存用于记录后向引用,并保持它们是最新的。
如果不需要一个后向引用,可通过使用非捕获组避免这种开销,例如,(?
…)替代(…)。
当需要一个完全匹配的后向引用时,有些人喜欢将正则表达式包装在一个捕获组中,这是不必要的,因为可以通过其他方法引用完全匹配。
例如,使用regex.exec()返回数组的第一个元素,或替换字符串中的$&
。
用非捕获组取代捕获组在Firefox浏览器中影响很小,但在其他浏览器上处理长字符串时影响很大。
(6)捕获感兴趣的文字,减少后处理
如果要引用匹配的一部分,应当通过一切手段,捕获那些片断,再使用后向引用处理。
例如,编写代码处理一个正则表达式所匹配的引号中的字符串内容,使用/"
([^"
]*)"
/之后再使用一次后向引用,而不是使用/"
/之后从结果中手工剥离引号。
当在循环中使用时,减少这方面的工作可以节省大量时间。
(7)暴露所需的字元
为帮助正则表达式引擎在如何优化查询例程时做出明智的决策,应尽量简单地判断出那些必需的字元。
当字元应用在子表达式或分支中时,正则表达式引擎很难判断它们是不是必需的,有些引擎并不做此方面的努力。
例如,正则表达式/^(ab|cd)/暴露它的字符串起始锚。
IE和Chrome浏览器会注意到这一点,并阻止正则表达式尝试查找字符串头端之后的匹配,从而使查找瞬间完成而不管字符串长度。
但是,由于等价正则表达式/(^ab|^cd)/不暴露它的^锚,IE无法应用同样的优化,最终无意义地搜索字符串并在每一个位置上匹配。
(8)使用适当的量词
正如建议45所讨论过的那样,“贪婪”量词和“懒惰”量词即使匹配同样的字符串,其查找匹配过程也是不同的。
在确保正确等价的前提下,使用更合适的量词类型(基于预期的回溯次数)可以显著提高性能,尤其在处理长字符串时。
(9)将正则表达式赋给变量,以重用它们
将正则表达式赋给变量以避免对它们重新编译。
有人使用正则表达式缓存池,以避免对给定的模板和标记组合进行多次编译。
不要过分担心,正则表达式编译得很快。
重要的是避免在循环体中重复编译正则表达式。
换句话说,不要这样做:
while(/regex1/.test(str1)){
/regex2/.exec(str2);
...
替代做法如下:
varregex1=/regex1/,
regex2=/regex2/;
while(regex1.test(str1)){
regex2.exec(str2);
(10)将复杂的正则表达式拆分为简单的片断
尽量避免一个正则表达式做太多的工作。
处理复杂的搜索问题需要将条件逻辑拆分为两个或多个正则表达式,这样更容易解决问题,通常也更高效,每个正则表达式只在最后的匹配结果中执行查找。
在一个模板中完成所有工作的正则表达式很难维护,而且容易引起回溯相关的问题。
建议47:
避免使用正则表达式的场景
正则表达式匹配速度是非常快的。
然而,当只搜索文字字符串时正则
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 改善 JavaScript 程序 188 建议 4150
![提示](https://static.bingdoc.com/images/bang_tan.gif)