建议36:警惕字符串连接操作(2)
先将两个小字符串合并起来,然后将结果返回给大字符串。创建中间字符串s1 + s2与两次复制大字符串相比,对性能的“冲击”要轻得多。
(2)编译期合并
在赋值表达式中所有字符串连接都属于编译期常量,Firefox自动地在编译过程中合并它们。在以下这个方法中可看到这一过程:
- function foldingDemo() {
- var str = "compile" + "time" + "folding";
- str += "this" + "works" + "too";
- strstr = str + "but" + "not" + "this";
- }
- alert(foldingDemo.toString());alert(foldingDemo.toString());
在Firefox中我们经常看到这种形式: - function foldingDemo() {
- var str = "compiletimefolding";
- str += "thisworkstoo";
- strstr = str + "but" + "not" + "this";
- }
- alert(foldingDemo.toString());alert(foldingDemo.toString());
当字符串是这样合并在一起时,由于运行时没有中间字符串,因此连接它们的时间和内存可以减少到零。这种功能非常了不起,但它并不经常起作用。
(3)数组联结
Array.prototype.join方法将数组的所有元素合并成一个字符串,并在每个元素之间插入一个分隔符字符串。如果传递一个空字符串作为分隔符,可以简单地将数组的所有元素连接起来。
在大多数浏览器上,数组联结比连接字符串的其他方法更慢,但事实上,作为一种补偿方法,在IE 7和更早的浏览器上它是连接大量字符串的唯一的高效途径。例如,下面的示例代码演示了可用数组联结解决的性能问题:
- var str = "I'm a thirty-five character string.", newStr = "", appends = 5000;
- while(appends--) {
- newStr += str;
- }
此代码连接5000 个长度为35 的字符串。执行以上代码后显示在IE 7中执行此测试所需的时间,从5000次连接开始,然后逐步增加连接数量。IE 7的连接算法要求浏览器在循环过程中反复地为越来越大的字符串复制和分配内存,结果是出现以平方关系递增的运行时间和内存消耗。
目前所有其他的浏览器(包括IE 8及其以上版本)在这个测试中表现良好,不会呈现平方关系的复杂性递增,这是真正的改善。然而,此程序演示了看似简单的字符串连接所产生的影响。5000次连接用去226ms已经是一个显著的性能冲击了,应当尽可能地缩减这一时间。锁定用户浏览器长达32 s,只是为了连接20 000 个短字符串,这对任何应用程序来说都是不能接受的。
如果使用数组联结生成同样的字符串,则代码如下:
- var str = "I'm a thirty-five character string.", strs = [], newStr, appends = 5000;
- while(appends--) {
- strs[strs.length] = str;
- }
- newStr = strs.join("");
上面代码优化的核心是避免重复的内存分配和复制越来越大的字符串。当联结一个数组时,浏览器宁愿分配足够大的内存用于存放整个字符串,也不会超过一次地复制最终字符串的同一部分。
原生字符串连接函数接受任意数目的参数,并将每一个参数都追加到调用函数的字符串,这是连接字符串最灵活的方法,因为可以利用它追加一个字符串,或者一次追加几个字符串,或者追加一个完整的字符串数组。
- strstr = str.concat(s1);
- strstr = str.concat(s1, s2, s3);
- str = String.prototype.concat.apply(str, array);
在大多数情况下,concat执行速度比简单的“+”和“+=”慢一些,而且在IE、Opera 和Chrome 浏览器上会大幅变慢。此外,虽然使用concat 合并数组中的所有字符串看起来和前面讨论的数组联结差不多,但是通常它更慢一些(在Opera浏览器上除外),而且它还潜伏着严重的性能问题,这与在IE 7 和更早版本中使用“+”和“+=”创建大字符串情况类似。
时间: 2024-09-14 18:58:14